Version in base suite: 6.8.7+dfsg1-0+deb13u1 Base version: wordpress_6.8.7+dfsg1-0+deb13u1 Target version: wordpress_6.8.10+dfsg1-0+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/w/wordpress/wordpress_6.8.7+dfsg1-0+deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/w/wordpress/wordpress_6.8.10+dfsg1-0+deb13u1.dsc debian/changelog | 30 + wp-admin/about.php | 60 ++ wp-admin/includes/ajax-actions.php | 24 wp-admin/includes/class-custom-image-header.php | 19 wp-admin/includes/media.php | 2 wp-admin/includes/plugin.php | 8 wp-admin/includes/post.php | 14 wp-admin/js/theme.js | 4 wp-admin/js/theme.min.js | 2 wp-includes/block-template-utils.php | 17 wp-includes/class-wp-image-editor-imagick.php | 260 +++++++++- wp-includes/class-wp-xmlrpc-server.php | 27 + wp-includes/customize/class-wp-customize-header-image-setting.php | 170 ++++++ wp-includes/formatting.php | 2 wp-includes/html-api/class-wp-html-tag-processor.php | 2 wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php | 71 ++ wp-includes/template.php | 78 ++- wp-includes/theme.php | 10 wp-includes/version.php | 2 19 files changed, 749 insertions(+), 53 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpeieyxsnc/wordpress_6.8.7+dfsg1-0+deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpeieyxsnc/wordpress_6.8.10+dfsg1-0+deb13u1.dsc: no acceptable signature found diff -Nru wordpress-6.8.7+dfsg1/debian/changelog wordpress-6.8.10+dfsg1/debian/changelog --- wordpress-6.8.7+dfsg1/debian/changelog 2026-08-07 08:13:22.000000000 +0000 +++ wordpress-6.8.10+dfsg1/debian/changelog 2026-09-23 10:30:44.000000000 +0000 @@ -1,9 +1,37 @@ +wordpress (6.8.10+dfsg1-0+deb13u1) trixie-security; urgency=high + + * Three security updates + * 6.8.10 backport of security release 7.1.2 Closes: #114873 + CVE-2026-87902: Unauthenticated path traversal in page-template resolution + leading to conditional RCE. + * 6.8.9 backport of security release 7.1.1 Closes: #1148368 + Stored XSS in wpautop() allows an unauthenticated visitor to inject + script (subject to comment approval). CVE-2026-93485 + API: set_modifiable_text() allows breaking out of a comment via + abrupt-closing sequences. + Stored XSS in some themes that support custom headers. + Specially crafted URLs can automatically install and preview an + inactive theme from WordPress.org. (Click2Shell) + Site Administrator can network-activate an installed Network-only plugin. + Authenticated Path Traversal in WP REST Templates Controller. + XML-RPC can be used to publish customize_changeset posts that bypass + checks for edit_css. + Contributor+ Arbitrary Post Overwrite. + Missing read_post check in attachment_submitbox_metadata() leaks a + private parent-post title. + Missing Authorization leads to Draft/Pending Post Slug Disclosure by + Contributor+. + Comments, including notes, can be reparented by any authenticated user. + * 6.8.8 backport of security release 7.0.4 + Fixes: CVE-2026-65640 Closes: #1144955 + + -- Craig Small Wed, 23 Sep 2026 20:30:44 +1000 + wordpress (6.8.7+dfsg1-0+deb13u1) trixie-security; urgency=medium * New upstream security release CVE-2026-64638 fix XSS in login that leads to RCE Closes: #1143843 - -- Craig Small Fri, 07 Aug 2026 18:13:22 +1000 wordpress (6.8.6+dfsg1-0+deb13u1) trixie-security; urgency=medium diff -Nru wordpress-6.8.7+dfsg1/wp-admin/about.php wordpress-6.8.10+dfsg1/wp-admin/about.php --- wordpress-6.8.7+dfsg1/wp-admin/about.php 2026-08-06 18:10:34.000000000 +0000 +++ wordpress-6.8.10+dfsg1/wp-admin/about.php 2026-09-22 13:00:22.000000000 +0000 @@ -61,6 +61,66 @@ Version %s addressed one security issue.' ), + '6.8.10' + ); + ?> + the release notes.' ), + sprintf( + /* translators: %s: WordPress version. */ + esc_url( __( 'https://wordpress.org/support/wordpress-version/version-%s/' ) ), + sanitize_title( '6.8.10' ) + ) + ); + ?> +

+

+ Version %s addressed some security issues.' ), + '6.8.9' + ); + ?> + the release notes.' ), + sprintf( + /* translators: %s: WordPress version. */ + esc_url( __( 'https://wordpress.org/support/wordpress-version/version-%s/' ) ), + sanitize_title( '6.8.9' ) + ) + ); + ?> +

+

+ Version %s addressed some security issues.' ), + '6.8.8' + ); + ?> + the release notes.' ), + sprintf( + /* translators: %s: WordPress version. */ + esc_url( __( 'https://wordpress.org/support/wordpress-version/version-%s/' ) ), + sanitize_title( '6.8.8' ) + ) + ); + ?> +

+

+ Version %s addressed some security issues.' ), '6.8.7' ); diff -Nru wordpress-6.8.7+dfsg1/wp-admin/includes/ajax-actions.php wordpress-6.8.10+dfsg1/wp-admin/includes/ajax-actions.php --- wordpress-6.8.7+dfsg1/wp-admin/includes/ajax-actions.php 2024-10-25 20:26:20.107352000 +0000 +++ wordpress-6.8.10+dfsg1/wp-admin/includes/ajax-actions.php 2026-09-17 18:21:54.640900000 +0000 @@ -2032,6 +2032,13 @@ function wp_ajax_get_permalink() { check_ajax_referer( 'getpermalink', 'getpermalinknonce' ); $post_id = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0; + if ( ! $post_id ) { + // Bypass call to get_preview_post_link() for unspecified post ID. + wp_die( '' ); + } + if ( ! current_user_can( 'edit_post', $post_id ) ) { + wp_die( -1 ); + } wp_die( get_preview_post_link( $post_id ) ); } @@ -2043,8 +2050,15 @@ function wp_ajax_sample_permalink() { check_ajax_referer( 'samplepermalink', 'samplepermalinknonce' ); $post_id = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0; - $title = isset( $_POST['new_title'] ) ? $_POST['new_title'] : ''; - $slug = isset( $_POST['new_slug'] ) ? $_POST['new_slug'] : null; + if ( ! $post_id ) { + // Bypass call to get_sample_permalink_html() for unspecified post ID. + wp_die( '' ); + } + if ( ! current_user_can( 'edit_post', $post_id ) ) { + wp_die( -1 ); + } + $title = isset( $_POST['new_title'] ) ? $_POST['new_title'] : ''; + $slug = isset( $_POST['new_slug'] ) ? $_POST['new_slug'] : null; wp_die( get_sample_permalink_html( $post_id, $title, $slug ) ); } @@ -4578,6 +4592,12 @@ wp_send_json_error( $status ); } + // A network-only plugin is activated for the entire network. + if ( is_multisite() && is_network_only_plugin( $status['plugin'] ) && ! current_user_can( 'manage_network_plugins' ) ) { + $status['errorMessage'] = __( 'Sorry, you are not allowed to activate this plugin.' ); + wp_send_json_error( $status ); + } + if ( is_plugin_active( $status['plugin'] ) ) { $status['errorMessage'] = sprintf( /* translators: %s: Plugin name. */ diff -Nru wordpress-6.8.7+dfsg1/wp-admin/includes/class-custom-image-header.php wordpress-6.8.10+dfsg1/wp-admin/includes/class-custom-image-header.php --- wordpress-6.8.7+dfsg1/wp-admin/includes/class-custom-image-header.php 2025-02-08 15:53:17.750427000 +0000 +++ wordpress-6.8.10+dfsg1/wp-admin/includes/class-custom-image-header.php 2026-09-17 18:21:54.640900000 +0000 @@ -572,7 +572,7 @@ $header_image_style .= 'height:' . $custom_header->height . 'px;'; } ?> -

+
$choice['attachment_id'], 'url' => $choice['url'], 'thumbnail_url' => $choice['url'], - 'height' => $choice['height'], - 'width' => $choice['width'], + 'height' => absint( $choice['height'] ), + 'width' => absint( $choice['width'] ), ); update_post_meta( $choice['attachment_id'], '_wp_attachment_is_custom_header', get_stylesheet() ); @@ -1216,7 +1216,13 @@ } } - set_theme_mod( 'header_image', sanitize_url( $header_image_data['url'] ) ); + $header_image_data['url'] = sanitize_url( $header_image_data['url'] ); + + if ( isset( $header_image_data['thumbnail_url'] ) ) { + $header_image_data['thumbnail_url'] = sanitize_url( $header_image_data['thumbnail_url'] ); + } + + set_theme_mod( 'header_image', $header_image_data['url'] ); set_theme_mod( 'header_image_data', $header_image_data ); } @@ -1583,9 +1589,8 @@ $alt_text_key = '_wp_attachment_image_alt'; foreach ( $header_images as &$header_image ) { - $header_meta = get_post_meta( $header_image['attachment_id'] ); - $header_image['timestamp'] = isset( $header_meta[ $timestamp_key ] ) ? $header_meta[ $timestamp_key ] : ''; - $header_image['alt_text'] = isset( $header_meta[ $alt_text_key ] ) ? $header_meta[ $alt_text_key ] : ''; + $header_image['timestamp'] = get_post_meta( $header_image['attachment_id'], $timestamp_key, true ); + $header_image['alt_text'] = get_post_meta( $header_image['attachment_id'], $alt_text_key, true ); } return $header_images; diff -Nru wordpress-6.8.7+dfsg1/wp-admin/includes/media.php wordpress-6.8.10+dfsg1/wp-admin/includes/media.php --- wordpress-6.8.7+dfsg1/wp-admin/includes/media.php 2025-03-25 14:07:53.288429000 +0000 +++ wordpress-6.8.10+dfsg1/wp-admin/includes/media.php 2026-09-17 18:21:54.640900000 +0000 @@ -3348,7 +3348,7 @@ post_parent ) { $post_parent = get_post( $post->post_parent ); - if ( $post_parent ) { + if ( $post_parent && current_user_can( 'read_post', $post_parent->ID ) ) { $uploaded_to_title = $post_parent->post_title ? $post_parent->post_title : __( '(no title)' ); $uploaded_to_link = get_edit_post_link( $post->post_parent, 'raw' ); ?> diff -Nru wordpress-6.8.7+dfsg1/wp-admin/includes/plugin.php wordpress-6.8.10+dfsg1/wp-admin/includes/plugin.php --- wordpress-6.8.7+dfsg1/wp-admin/includes/plugin.php 2024-12-05 12:13:17.434857000 +0000 +++ wordpress-6.8.10+dfsg1/wp-admin/includes/plugin.php 2026-09-17 18:21:54.640900000 +0000 @@ -598,11 +598,17 @@ * Checks for "Site Wide Only: true" for backward compatibility. * * @since 3.0.0 + * @since 7.1.1 The `$plugin` path is normalized with `plugin_basename()` and `trim()`, + * matching how `activate_plugin()` resolves it. * - * @param string $plugin Path to the plugin file relative to the plugins directory. + * @param string $plugin Path to the plugin file. Accepts a path relative to the plugins + * directory, or an absolute path, with or without surrounding whitespace. * @return bool True if plugin is network only, false otherwise. */ function is_network_only_plugin( $plugin ) { + // Normalize the path the same way activate_plugin() does, so both agree on the file. + $plugin = plugin_basename( trim( $plugin ) ); + $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); if ( $plugin_data ) { return $plugin_data['Network']; diff -Nru wordpress-6.8.7+dfsg1/wp-admin/includes/post.php wordpress-6.8.10+dfsg1/wp-admin/includes/post.php --- wordpress-6.8.7+dfsg1/wp-admin/includes/post.php 2025-03-10 20:03:25.224092000 +0000 +++ wordpress-6.8.10+dfsg1/wp-admin/includes/post.php 2026-09-17 18:21:54.640900000 +0000 @@ -24,6 +24,20 @@ $post_data = &$_POST; } + /* + * A raw `ID` on the create path (no `post_ID`) is an attempt to overwrite an + * existing post while bypassing the per-post capability checks below, which only + * run on the update path. Reject it outright: legitimate post creation never + * carries an `ID`. + */ + if ( ! $update && ! empty( $post_data['ID'] ) ) { + if ( 'page' === $post_data['post_type'] ) { + return new WP_Error( 'edit_others_pages', __( 'Sorry, you are not allowed to edit pages as this user.' ) ); + } else { + return new WP_Error( 'edit_others_posts', __( 'Sorry, you are not allowed to edit posts as this user.' ) ); + } + } + if ( $update ) { $post_data['ID'] = (int) $post_data['post_ID']; } diff -Nru wordpress-6.8.7+dfsg1/wp-admin/js/theme.js wordpress-6.8.10+dfsg1/wp-admin/js/theme.js --- wordpress-6.8.7+dfsg1/wp-admin/js/theme.js 2025-03-10 18:16:27.024762000 +0000 +++ wordpress-6.8.10+dfsg1/wp-admin/js/theme.js 2026-09-17 18:21:54.640900000 +0000 @@ -2027,9 +2027,9 @@ self.view.collection.query( request ); self.view.collection.trigger( 'update' ); - // Open the theme preview. + // Open the theme preview. The slug comes from the URL, so escape it. self.view.collection.once( 'query:success', function() { - $( 'div[data-slug="' + slug + '"]' ).trigger( 'click' ); + $( 'div.theme[data-slug="' + $.escapeSelector( slug ) + '"]' ).trigger( 'click' ); }); } diff -Nru wordpress-6.8.7+dfsg1/wp-admin/js/theme.min.js wordpress-6.8.10+dfsg1/wp-admin/js/theme.min.js --- wordpress-6.8.7+dfsg1/wp-admin/js/theme.min.js 2025-03-10 18:16:27.024762000 +0000 +++ wordpress-6.8.10+dfsg1/wp-admin/js/theme.min.js 2026-09-17 18:21:54.640900000 +0000 @@ -1,2 +1,2 @@ /*! This file is auto-generated */ -window.wp=window.wp||{},function(n){var o,a;function e(e,t){Backbone.history._hasPushState&&Backbone.Router.prototype.navigate.call(this,e,t)}(o=wp.themes=wp.themes||{}).data=_wpThemeSettings,a=o.data.l10n,o.isInstall=!!o.data.settings.isInstall,_.extend(o,{model:{},view:{},routes:{},router:{},template:wp.template}),o.Model=Backbone.Model.extend({initialize:function(){var e;this.get("slug")&&(-1!==_.indexOf(o.data.installedThemes,this.get("slug"))&&this.set({installed:!0}),o.data.activeTheme===this.get("slug"))&&this.set({active:!0}),this.set({id:this.get("slug")||this.get("id")}),this.has("sections")&&(e=this.get("sections").description,this.set({description:e}))}}),o.view.Appearance=wp.Backbone.View.extend({el:"#wpbody-content .wrap .theme-browser",window:n(window),page:0,initialize:function(e){_.bindAll(this,"scroller"),this.SearchView=e.SearchView||o.view.Search,this.window.on("scroll",_.throttle(this.scroller,300))},render:function(){this.view=new o.view.Themes({collection:this.collection,parent:this}),this.search(),this.$el.removeClass("search-loading"),this.view.render(),this.$el.empty().append(this.view.el).addClass("rendered")},searchContainer:n(".search-form"),search:function(){var e;1!==o.data.themes.length&&(e=new this.SearchView({collection:this.collection,parent:this}),(this.SearchView=e).render(),this.searchContainer.find(".search-box").append(n.parseHTML('")).append(e.el),this.searchContainer.on("submit",function(e){e.preventDefault()}))},scroller:function(){var e=this,t=this.window.scrollTop()+e.window.height(),e=e.$el.offset().top+e.$el.outerHeight(!1)-e.window.height();Math.round(.9*e)]+)>)/gi,""),r=e.get("description").replace(/(<([^>]+)>)/gi,""),a=e.get("author").replace(/(<([^>]+)>)/gi,""),s=_.union([s,e.get("id"),r,a,e.get("tags")]),i.test(e.get("author"))&&2

'+a.addNew+"

"),this.parent.page++)},currentTheme:function(){var e=this.collection.findWhere({active:!0});e&&(this.collection.remove(e),this.collection.add(e,{at:0}))},setView:function(e){return e},expand:function(e){var t,i=this;this.model=i.collection.get(e),o.router.navigate(o.router.baseUrl(o.router.themePath+this.model.id)),this.setView("detail"),n("body").addClass("modal-open"),this.overlay=new o.view.Details({model:i.model}),this.overlay.render(),this.model.get("hasUpdate")&&(e=n('[data-slug="'+this.model.id+'"]'),t=n(this.overlay.el),e.find(".updating-message").length?(t.find(".notice-warning h3").remove(),t.find(".notice-warning").removeClass("notice-large").addClass("updating-message").find("p").text(wp.updates.l10n.updating)):e.find(".notice-error").length&&t.find(".notice-warning").remove()),this.$overlay.html(this.overlay.el),this.listenTo(this.overlay,"theme:next",function(){i.next([i.model.cid])}).listenTo(this.overlay,"theme:previous",function(){i.previous([i.model.cid])})},next:function(e){e=this.collection.get(e[0]),e=this.collection.at(this.collection.indexOf(e)+1);void 0!==e&&(this.overlay.closeOverlay(),this.theme.trigger("theme:expand",e.cid))},previous:function(e){e=this.collection.get(e[0]),e=this.collection.at(this.collection.indexOf(e)-1);void 0!==e&&(this.overlay.closeOverlay(),this.theme.trigger("theme:expand",e.cid))},announceSearchResults:function(e){0===e?wp.a11y.speak(a.noThemesFound):wp.a11y.speak(a.themesFound.replace("%d",e))}}),o.view.Search=wp.Backbone.View.extend({tagName:"input",className:"wp-filter-search",id:"wp-filter-search-input",searching:!1,attributes:{type:"search","aria-describedby":"live-search-desc"},events:{input:"search",keyup:"search",blur:"pushState"},initialize:function(e){this.parent=e.parent,this.listenTo(this.parent,"theme:close",function(){this.searching=!1})},search:function(e){"keyup"===e.type&&27===e.which&&(e.target.value=""),this.doSearch(e)},doSearch:function(e){var t={};this.collection.doSearch(e.target.value.replace(/\+/g," ")),this.searching&&13!==e.which?t.replace=!0:this.searching=!0,e.target.value?o.router.navigate(o.router.baseUrl(o.router.searchPath+e.target.value),t):o.router.navigate(o.router.baseUrl(""))},pushState:function(e){var t=o.router.baseUrl("");e.target.value&&(t=o.router.baseUrl(o.router.searchPath+encodeURIComponent(e.target.value))),this.searching=!1,o.router.navigate(t)}}),o.Router=Backbone.Router.extend({routes:{"themes.php?theme=:slug":"theme","themes.php?search=:query":"search","themes.php?s=:query":"search","themes.php":"themes","":"themes"},baseUrl:function(e){return"themes.php"+e},themePath:"?theme=",searchPath:"?search=",search:function(e){n(".wp-filter-search").val(e.replace(/\+/g," "))},themes:function(){n(".wp-filter-search").val("")},navigate:e}),o.Run={init:function(){this.themes=new o.Collection(o.data.themes),this.view=new o.view.Appearance({collection:this.themes}),this.render(),this.view.SearchView.doSearch=_.debounce(this.view.SearchView.doSearch,500)},render:function(){this.view.render(),this.routes(),Backbone.History.started&&Backbone.history.stop(),Backbone.history.start({root:o.data.settings.adminUrl,pushState:!0,hashChange:!1})},routes:function(){var t=this;o.router=new o.Router,o.router.on("route:theme",function(e){t.view.view.expand(e)}),o.router.on("route:themes",function(){t.themes.doSearch(""),t.view.trigger("theme:close")}),o.router.on("route:search",function(){n(".wp-filter-search").trigger("keyup")}),this.extraRoutes()},extraRoutes:function(){return!1}},o.view.InstallerSearch=o.view.Search.extend({events:{input:"search",keyup:"search"},terms:"",search:function(e){("keyup"!==e.type||9!==e.which&&16!==e.which)&&(this.collection=this.options.parent.view.collection,"keyup"===e.type&&27===e.which&&(e.target.value=""),this.doSearch(e.target.value))},doSearch:function(e){var t={};this.terms!==e&&(this.terms=e,"author:"===(t.search=e).substring(0,7)&&(t.search="",t.author=e.slice(7)),"tag:"===e.substring(0,4)&&(t.search="",t.tag=[e.slice(4)]),n(".filter-links li > a.current").removeClass("current").removeAttr("aria-current"),n("body").removeClass("show-filters filters-applied show-favorites-form"),n(".drawer-toggle").attr("aria-expanded","false"),this.collection.query(t),o.router.navigate(o.router.baseUrl(o.router.searchPath+encodeURIComponent(e)),{replace:!0}))}}),o.view.Installer=o.view.Appearance.extend({el:"#wpbody-content .wrap",events:{"click .filter-links li > a":"onSort","click .theme-filter":"onFilter","click .drawer-toggle":"moreFilters","click .filter-drawer .apply-filters":"applyFilters",'click .filter-group [type="checkbox"]':"addFilter","click .filter-drawer .clear-filters":"clearFilters","click .edit-filters":"backToFilters","click .favorites-form-submit":"saveUsername","keyup #wporg-username-input":"saveUsername"},render:function(){var e=this;this.search(),this.uploader(),this.collection=new o.Collection,this.listenTo(this,"theme:end",function(){e.collection.loadingThemes||(e.collection.loadingThemes=!0,e.collection.currentQuery.page++,_.extend(e.collection.currentQuery.request,{page:e.collection.currentQuery.page}),e.collection.query(e.collection.currentQuery.request))}),this.listenTo(this.collection,"query:success",function(){n("body").removeClass("loading-content"),n(".theme-browser").find("div.error").remove()}),this.listenTo(this.collection,"query:fail",function(){n("body").removeClass("loading-content"),n(".theme-browser").find("div.error").remove(),n(".theme-browser").find("div.themes").before('

'+a.error+'

"),n(".theme-browser .error .try-again").on("click",function(e){e.preventDefault(),n("input.wp-filter-search").trigger("input")})}),this.view&&this.view.remove(),this.view=new o.view.Themes({collection:this.collection,parent:this}),this.page=0,this.$el.find(".themes").remove(),this.view.render(),this.$el.find(".theme-browser").append(this.view.el).addClass("rendered")},browse:function(e){"block-themes"===e?this.collection.query({tag:"full-site-editing"}):this.collection.query({browse:e})},onSort:function(e){var t=n(e.target),i=t.data("sort");e.preventDefault(),n("body").removeClass("filters-applied show-filters"),n(".drawer-toggle").attr("aria-expanded","false"),t.hasClass(this.activeClass)||(this.sort(i),o.router.navigate(o.router.baseUrl(o.router.browsePath+i)))},sort:function(e){this.clearSearch(),o.router.selectedTab=e,n(".filter-links li > a, .theme-filter").removeClass(this.activeClass).removeAttr("aria-current"),n('[data-sort="'+e+'"]').addClass(this.activeClass).attr("aria-current","page"),"favorites"===e?n("body").addClass("show-favorites-form"):n("body").removeClass("show-favorites-form"),this.browse(e)},onFilter:function(e){var e=n(e.target),t=e.data("filter");e.hasClass(this.activeClass)||(n(".filter-links li > a, .theme-section").removeClass(this.activeClass).removeAttr("aria-current"),e.addClass(this.activeClass).attr("aria-current","page"),t&&(t=_.union([t,this.filtersChecked()]),this.collection.query({tag:[t]})))},addFilter:function(){this.filtersChecked()},applyFilters:function(e){var t,i=this.filtersChecked(),s={tag:i},r=n(".filtered-by .tags");e&&e.preventDefault(),i?(n("body").addClass("filters-applied"),n(".filter-links li > a.current").removeClass("current").removeAttr("aria-current"),r.empty(),_.each(i,function(e){t=n('label[for="filter-id-'+e+'"]').text(),r.append(''+t+"")}),this.collection.query(s)):wp.a11y.speak(a.selectFeatureFilter)},saveUsername:function(e){var t=n("#wporg-username-input").val(),i=n("#wporg-username-nonce").val(),s={browse:"favorites",user:t},r=this;if(e&&e.preventDefault(),"keyup"!==e.type||13===e.which)return wp.ajax.send("save-wporg-username",{data:{_wpnonce:i,username:t},success:function(){r.collection.query(s)}})},filtersChecked:function(){var e=n(".filter-group").find(":checkbox"),t=[];return _.each(e.filter(":checked"),function(e){t.push(n(e).prop("value"))}),0===t.length?(n(".filter-drawer .apply-filters").find("span").text(""),n(".filter-drawer .clear-filters").hide(),n("body").removeClass("filters-applied"),!1):(n(".filter-drawer .apply-filters").find("span").text(t.length),n(".filter-drawer .clear-filters").css("display","inline-block"),t)},activeClass:"current",uploader:function(){var e=n(".upload-view-toggle"),t=n(document.body);e.on("click",function(){t.toggleClass("show-upload-view"),e.attr("aria-expanded",t.hasClass("show-upload-view"))})},moreFilters:function(e){var t=n("body"),i=n(".drawer-toggle");if(e.preventDefault(),t.hasClass("filters-applied"))return this.backToFilters();this.clearSearch(),o.router.navigate(o.router.baseUrl("")),t.toggleClass("show-filters"),i.attr("aria-expanded",t.hasClass("show-filters"))},clearFilters:function(e){var t=n(".filter-group").find(":checkbox"),i=this;e.preventDefault(),_.each(t.filter(":checked"),function(e){return n(e).prop("checked",!1),i.filtersChecked()})},backToFilters:function(e){e&&e.preventDefault(),n("body").removeClass("filters-applied")},clearSearch:function(){n("#wp-filter-search-input").val("")}}),o.InstallerRouter=Backbone.Router.extend({routes:{"theme-install.php?theme=:slug":"preview","theme-install.php?browse=:sort":"sort","theme-install.php?search=:query":"search","theme-install.php":"sort"},baseUrl:function(e){return"theme-install.php"+e},themePath:"?theme=",browsePath:"?browse=",searchPath:"?search=",search:function(e){n(".wp-filter-search").val(e.replace(/\+/g," "))},navigate:e}),o.RunInstaller={init:function(){this.view=new o.view.Installer({section:"popular",SearchView:o.view.InstallerSearch}),this.render(),this.view.SearchView.doSearch=_.debounce(this.view.SearchView.doSearch,500)},render:function(){this.view.render(),this.routes(),Backbone.History.started&&Backbone.history.stop(),Backbone.history.start({root:o.data.settings.adminUrl,pushState:!0,hashChange:!1})},routes:function(){var t=this,i={};o.router=new o.InstallerRouter,o.router.on("route:preview",function(e){o.preview&&(o.preview.undelegateEvents(),o.preview.unbind()),t.view.view.theme&&t.view.view.theme.preview?(t.view.view.theme.model=t.view.collection.findWhere({slug:e}),t.view.view.theme.preview()):(i.theme=e,t.view.collection.query(i),t.view.collection.trigger("update"),t.view.collection.once("query:success",function(){n('div[data-slug="'+e+'"]').trigger("click")}))}),o.router.on("route:sort",function(e){e||(e="popular",o.router.navigate(o.router.baseUrl("?browse=popular"),{replace:!0})),t.view.sort(e),o.preview&&o.preview.close()}),o.router.on("route:search",function(){n(".wp-filter-search").trigger("focus").trigger("keyup")}),this.extraRoutes()},extraRoutes:function(){return!1}},n(function(){(o.isInstall?o.RunInstaller:o.Run).init(),n(document.body).on("click",".load-customize",function(){var e=n(this),t=document.createElement("a");t.href=e.prop("href"),t.search=n.param(_.extend(wp.customize.utils.parseQueryString(t.search.substr(1)),{return:window.location.href})),e.prop("href",t.href)}),n(".broken-themes .delete-theme").on("click",function(){return confirm(_wpThemeSettings.settings.confirmDelete)})})}(jQuery),jQuery(function(r){window.tb_position=function(){var e=r("#TB_window"),t=r(window).width(),i=r(window).height(),t=1040'+a.search+"")).append(e.el),this.searchContainer.on("submit",function(e){e.preventDefault()}))},scroller:function(){var e=this,t=this.window.scrollTop()+e.window.height(),e=e.$el.offset().top+e.$el.outerHeight(!1)-e.window.height();Math.round(.9*e)]+)>)/gi,""),r=e.get("description").replace(/(<([^>]+)>)/gi,""),a=e.get("author").replace(/(<([^>]+)>)/gi,""),s=_.union([s,e.get("id"),r,a,e.get("tags")]),i.test(e.get("author"))&&2

'+a.addNew+"

"),this.parent.page++)},currentTheme:function(){var e=this.collection.findWhere({active:!0});e&&(this.collection.remove(e),this.collection.add(e,{at:0}))},setView:function(e){return e},expand:function(e){var t,i=this;this.model=i.collection.get(e),o.router.navigate(o.router.baseUrl(o.router.themePath+this.model.id)),this.setView("detail"),n("body").addClass("modal-open"),this.overlay=new o.view.Details({model:i.model}),this.overlay.render(),this.model.get("hasUpdate")&&(e=n('[data-slug="'+this.model.id+'"]'),t=n(this.overlay.el),e.find(".updating-message").length?(t.find(".notice-warning h3").remove(),t.find(".notice-warning").removeClass("notice-large").addClass("updating-message").find("p").text(wp.updates.l10n.updating)):e.find(".notice-error").length&&t.find(".notice-warning").remove()),this.$overlay.html(this.overlay.el),this.listenTo(this.overlay,"theme:next",function(){i.next([i.model.cid])}).listenTo(this.overlay,"theme:previous",function(){i.previous([i.model.cid])})},next:function(e){e=this.collection.get(e[0]),e=this.collection.at(this.collection.indexOf(e)+1);void 0!==e&&(this.overlay.closeOverlay(),this.theme.trigger("theme:expand",e.cid))},previous:function(e){e=this.collection.get(e[0]),e=this.collection.at(this.collection.indexOf(e)-1);void 0!==e&&(this.overlay.closeOverlay(),this.theme.trigger("theme:expand",e.cid))},announceSearchResults:function(e){0===e?wp.a11y.speak(a.noThemesFound):wp.a11y.speak(a.themesFound.replace("%d",e))}}),o.view.Search=wp.Backbone.View.extend({tagName:"input",className:"wp-filter-search",id:"wp-filter-search-input",searching:!1,attributes:{type:"search","aria-describedby":"live-search-desc"},events:{input:"search",keyup:"search",blur:"pushState"},initialize:function(e){this.parent=e.parent,this.listenTo(this.parent,"theme:close",function(){this.searching=!1})},search:function(e){"keyup"===e.type&&27===e.which&&(e.target.value=""),this.doSearch(e)},doSearch:function(e){var t={};this.collection.doSearch(e.target.value.replace(/\+/g," ")),this.searching&&13!==e.which?t.replace=!0:this.searching=!0,e.target.value?o.router.navigate(o.router.baseUrl(o.router.searchPath+e.target.value),t):o.router.navigate(o.router.baseUrl(""))},pushState:function(e){var t=o.router.baseUrl("");e.target.value&&(t=o.router.baseUrl(o.router.searchPath+encodeURIComponent(e.target.value))),this.searching=!1,o.router.navigate(t)}}),o.Router=Backbone.Router.extend({routes:{"themes.php?theme=:slug":"theme","themes.php?search=:query":"search","themes.php?s=:query":"search","themes.php":"themes","":"themes"},baseUrl:function(e){return"themes.php"+e},themePath:"?theme=",searchPath:"?search=",search:function(e){n(".wp-filter-search").val(e.replace(/\+/g," "))},themes:function(){n(".wp-filter-search").val("")},navigate:e}),o.Run={init:function(){this.themes=new o.Collection(o.data.themes),this.view=new o.view.Appearance({collection:this.themes}),this.render(),this.view.SearchView.doSearch=_.debounce(this.view.SearchView.doSearch,500)},render:function(){this.view.render(),this.routes(),Backbone.History.started&&Backbone.history.stop(),Backbone.history.start({root:o.data.settings.adminUrl,pushState:!0,hashChange:!1})},routes:function(){var t=this;o.router=new o.Router,o.router.on("route:theme",function(e){t.view.view.expand(e)}),o.router.on("route:themes",function(){t.themes.doSearch(""),t.view.trigger("theme:close")}),o.router.on("route:search",function(){n(".wp-filter-search").trigger("keyup")}),this.extraRoutes()},extraRoutes:function(){return!1}},o.view.InstallerSearch=o.view.Search.extend({events:{input:"search",keyup:"search"},terms:"",search:function(e){("keyup"!==e.type||9!==e.which&&16!==e.which)&&(this.collection=this.options.parent.view.collection,"keyup"===e.type&&27===e.which&&(e.target.value=""),this.doSearch(e.target.value))},doSearch:function(e){var t={};this.terms!==e&&(this.terms=e,"author:"===(t.search=e).substring(0,7)&&(t.search="",t.author=e.slice(7)),"tag:"===e.substring(0,4)&&(t.search="",t.tag=[e.slice(4)]),n(".filter-links li > a.current").removeClass("current").removeAttr("aria-current"),n("body").removeClass("show-filters filters-applied show-favorites-form"),n(".drawer-toggle").attr("aria-expanded","false"),this.collection.query(t),o.router.navigate(o.router.baseUrl(o.router.searchPath+encodeURIComponent(e)),{replace:!0}))}}),o.view.Installer=o.view.Appearance.extend({el:"#wpbody-content .wrap",events:{"click .filter-links li > a":"onSort","click .theme-filter":"onFilter","click .drawer-toggle":"moreFilters","click .filter-drawer .apply-filters":"applyFilters",'click .filter-group [type="checkbox"]':"addFilter","click .filter-drawer .clear-filters":"clearFilters","click .edit-filters":"backToFilters","click .favorites-form-submit":"saveUsername","keyup #wporg-username-input":"saveUsername"},render:function(){var e=this;this.search(),this.uploader(),this.collection=new o.Collection,this.listenTo(this,"theme:end",function(){e.collection.loadingThemes||(e.collection.loadingThemes=!0,e.collection.currentQuery.page++,_.extend(e.collection.currentQuery.request,{page:e.collection.currentQuery.page}),e.collection.query(e.collection.currentQuery.request))}),this.listenTo(this.collection,"query:success",function(){n("body").removeClass("loading-content"),n(".theme-browser").find("div.error").remove()}),this.listenTo(this.collection,"query:fail",function(){n("body").removeClass("loading-content"),n(".theme-browser").find("div.error").remove(),n(".theme-browser").find("div.themes").before('

'+a.error+'

"),n(".theme-browser .error .try-again").on("click",function(e){e.preventDefault(),n("input.wp-filter-search").trigger("input")})}),this.view&&this.view.remove(),this.view=new o.view.Themes({collection:this.collection,parent:this}),this.page=0,this.$el.find(".themes").remove(),this.view.render(),this.$el.find(".theme-browser").append(this.view.el).addClass("rendered")},browse:function(e){"block-themes"===e?this.collection.query({tag:"full-site-editing"}):this.collection.query({browse:e})},onSort:function(e){var t=n(e.target),i=t.data("sort");e.preventDefault(),n("body").removeClass("filters-applied show-filters"),n(".drawer-toggle").attr("aria-expanded","false"),t.hasClass(this.activeClass)||(this.sort(i),o.router.navigate(o.router.baseUrl(o.router.browsePath+i)))},sort:function(e){this.clearSearch(),o.router.selectedTab=e,n(".filter-links li > a, .theme-filter").removeClass(this.activeClass).removeAttr("aria-current"),n('[data-sort="'+e+'"]').addClass(this.activeClass).attr("aria-current","page"),"favorites"===e?n("body").addClass("show-favorites-form"):n("body").removeClass("show-favorites-form"),this.browse(e)},onFilter:function(e){var e=n(e.target),t=e.data("filter");e.hasClass(this.activeClass)||(n(".filter-links li > a, .theme-section").removeClass(this.activeClass).removeAttr("aria-current"),e.addClass(this.activeClass).attr("aria-current","page"),t&&(t=_.union([t,this.filtersChecked()]),this.collection.query({tag:[t]})))},addFilter:function(){this.filtersChecked()},applyFilters:function(e){var t,i=this.filtersChecked(),s={tag:i},r=n(".filtered-by .tags");e&&e.preventDefault(),i?(n("body").addClass("filters-applied"),n(".filter-links li > a.current").removeClass("current").removeAttr("aria-current"),r.empty(),_.each(i,function(e){t=n('label[for="filter-id-'+e+'"]').text(),r.append(''+t+"")}),this.collection.query(s)):wp.a11y.speak(a.selectFeatureFilter)},saveUsername:function(e){var t=n("#wporg-username-input").val(),i=n("#wporg-username-nonce").val(),s={browse:"favorites",user:t},r=this;if(e&&e.preventDefault(),"keyup"!==e.type||13===e.which)return wp.ajax.send("save-wporg-username",{data:{_wpnonce:i,username:t},success:function(){r.collection.query(s)}})},filtersChecked:function(){var e=n(".filter-group").find(":checkbox"),t=[];return _.each(e.filter(":checked"),function(e){t.push(n(e).prop("value"))}),0===t.length?(n(".filter-drawer .apply-filters").find("span").text(""),n(".filter-drawer .clear-filters").hide(),n("body").removeClass("filters-applied"),!1):(n(".filter-drawer .apply-filters").find("span").text(t.length),n(".filter-drawer .clear-filters").css("display","inline-block"),t)},activeClass:"current",uploader:function(){var e=n(".upload-view-toggle"),t=n(document.body);e.on("click",function(){t.toggleClass("show-upload-view"),e.attr("aria-expanded",t.hasClass("show-upload-view"))})},moreFilters:function(e){var t=n("body"),i=n(".drawer-toggle");if(e.preventDefault(),t.hasClass("filters-applied"))return this.backToFilters();this.clearSearch(),o.router.navigate(o.router.baseUrl("")),t.toggleClass("show-filters"),i.attr("aria-expanded",t.hasClass("show-filters"))},clearFilters:function(e){var t=n(".filter-group").find(":checkbox"),i=this;e.preventDefault(),_.each(t.filter(":checked"),function(e){return n(e).prop("checked",!1),i.filtersChecked()})},backToFilters:function(e){e&&e.preventDefault(),n("body").removeClass("filters-applied")},clearSearch:function(){n("#wp-filter-search-input").val("")}}),o.InstallerRouter=Backbone.Router.extend({routes:{"theme-install.php?theme=:slug":"preview","theme-install.php?browse=:sort":"sort","theme-install.php?search=:query":"search","theme-install.php":"sort"},baseUrl:function(e){return"theme-install.php"+e},themePath:"?theme=",browsePath:"?browse=",searchPath:"?search=",search:function(e){n(".wp-filter-search").val(e.replace(/\+/g," "))},navigate:e}),o.RunInstaller={init:function(){this.view=new o.view.Installer({section:"popular",SearchView:o.view.InstallerSearch}),this.render(),this.view.SearchView.doSearch=_.debounce(this.view.SearchView.doSearch,500)},render:function(){this.view.render(),this.routes(),Backbone.History.started&&Backbone.history.stop(),Backbone.history.start({root:o.data.settings.adminUrl,pushState:!0,hashChange:!1})},routes:function(){var t=this,i={};o.router=new o.InstallerRouter,o.router.on("route:preview",function(e){o.preview&&(o.preview.undelegateEvents(),o.preview.unbind()),t.view.view.theme&&t.view.view.theme.preview?(t.view.view.theme.model=t.view.collection.findWhere({slug:e}),t.view.view.theme.preview()):(i.theme=e,t.view.collection.query(i),t.view.collection.trigger("update"),t.view.collection.once("query:success",function(){n('div.theme[data-slug="'+n.escapeSelector(e)+'"]').trigger("click")}))}),o.router.on("route:sort",function(e){e||(e="popular",o.router.navigate(o.router.baseUrl("?browse=popular"),{replace:!0})),t.view.sort(e),o.preview&&o.preview.close()}),o.router.on("route:search",function(){n(".wp-filter-search").trigger("focus").trigger("keyup")}),this.extraRoutes()},extraRoutes:function(){return!1}},n(function(){(o.isInstall?o.RunInstaller:o.Run).init(),n(document.body).on("click",".load-customize",function(){var e=n(this),t=document.createElement("a");t.href=e.prop("href"),t.search=n.param(_.extend(wp.customize.utils.parseQueryString(t.search.substr(1)),{return:window.location.href})),e.prop("href",t.href)}),n(".broken-themes .delete-theme").on("click",function(){return confirm(_wpThemeSettings.settings.confirmDelete)})})}(jQuery),jQuery(function(r){window.tb_position=function(){var e=r("#TB_window"),t=r(window).width(),i=r(window).height(),t=1040 $theme_dir ) { $template_base_paths = get_block_theme_folders( $theme_slug ); - $file_path = $theme_dir . '/' . $template_base_paths[ $template_type ] . '/' . $slug . '.html'; - if ( file_exists( $file_path ) ) { + $template_dir = $theme_dir . '/' . $template_base_paths[ $template_type ]; + $file_path = $template_dir . '/' . $slug . '.html'; + $template_file = realpath( $file_path ); + $template_root = realpath( $template_dir ); + + if ( + false !== $template_file && + false !== $template_root && + str_starts_with( + wp_normalize_path( $template_file ), + trailingslashit( wp_normalize_path( $template_root ) ) + ) + ) { $new_template_item = array( 'slug' => $slug, - 'path' => $file_path, + 'path' => $template_file, 'theme' => $theme_slug, 'type' => $template_type, ); diff -Nru wordpress-6.8.7+dfsg1/wp-includes/class-wp-image-editor-imagick.php wordpress-6.8.10+dfsg1/wp-includes/class-wp-image-editor-imagick.php --- wordpress-6.8.7+dfsg1/wp-includes/class-wp-image-editor-imagick.php 2025-03-18 23:25:31.323870000 +0000 +++ wordpress-6.8.10+dfsg1/wp-includes/class-wp-image-editor-imagick.php 2026-08-12 12:34:58.859815000 +0000 @@ -21,6 +21,28 @@ */ protected $image; + /** + * Temporarily stores stream image data while processing internally. + * + * @see self::pdf_load_source() + * + * @since 7.0.4 + * + * @var string|null + */ + private $stream_file_data = null; + + /** + * Temporarily stores the parsed given name for an image while processing internally. + * + * @see self::pdf_load_source() + * + * @since 7.0.4 + * + * @var string|null + */ + private $image_given_name = null; + public function __destruct() { if ( $this->image instanceof Imagick ) { // We don't need the original in memory anymore. @@ -130,30 +152,208 @@ return true; } - if ( ! is_file( $this->file ) && ! wp_is_stream( $this->file ) ) { + $is_stream = wp_is_stream( $this->file ); + $is_file = ! $is_stream && is_file( $this->file ); + + // Only allow loading files or streams. + if ( ! $is_file && ! $is_stream ) { return new WP_Error( 'error_loading_image', __( 'File does not exist?' ), $this->file ); } + // Establish the provided filename based on the kind of resource being loaded. + $given_filename = $this->file; + if ( 0 === strncasecmp( $given_filename, 'file://', 7 ) ) { + $given_filename = basename( substr( $given_filename, 7 ) ); // 7 is the strlen of 'file://'. + } elseif ( 1 === preg_match( '~^https?://~i', $this->file ) ) { + /* + * For URLs, it will be the final path segment. + * + * Example: + * + * https://wordpress.org/i/happy.png?size=40px + * ╰───────╯ + * this is the given filename + * + * If the stream returns a `Content-Disposition` header it would + * provide an alternative name, but this is used as a reasonable + * proxy to avoid adding the additional complexity of reading and + * parsing the returned HTTP headers. + */ + $url_path = wp_parse_url( $this->file, PHP_URL_PATH ); + + // This URL can not be parsed, so it is not a valid image resource. + if ( false === $url_path ) { + return new WP_Error( 'error_loading_image', __( 'File is not an image.' ), $this->file ); + } + + /** + * The URL has an empty path, so continue with an empty string. + * + * This is the case with a URL such as `https://example.com?file_id=123` + */ + if ( null === $url_path ) { + $url_path = ''; + } + + $last_path_at = strrpos( $url_path, '/' ); + $given_filename = is_int( $last_path_at ) ? substr( $url_path, $last_path_at + 1 ) : $url_path; + $given_filename = rawurldecode( $given_filename ); + } + + /* + * Strip off any potential `Imagick` format specifiers. + * + * If a real file exists with the identified format specifier, then + * `Imagick` may not treat it as a format, but WordPress will reject + * it anyway to avoid adding more complexity into this detection. + * + * `Imagick` reads only the first `FORMAT:` specifier on a name, but + * stripping a segment would promote a second specifier to the front + * of the name handed to `Imagick`, which would then honor it. + * + * Loop to capture all format specifiers for comparison. + * + * Exclude Windows drive-letter prefixes from here. + */ + $imagick_formats = array(); + while ( + false !== ( $format_ends_at = strpos( $given_filename, ':' ) ) && + 1 !== preg_match( '~^[a-z]:~i', $given_filename ) + ) { + $imagick_formats[] = strtoupper( substr( $given_filename, 0, $format_ends_at ) ); + $given_filename = substr( $given_filename, $format_ends_at + 1 ); + } + + $file_extension = strtolower( pathinfo( $given_filename, PATHINFO_EXTENSION ) ); + /* * Even though Imagick uses less PHP memory than GD, set higher limit * for users that have low PHP.ini limits. */ wp_raise_memory_limit( 'image' ); + /** + * Read the resource header for MIME sniffing. + * + * For files, which will be passed into Imagick by their file names, avoid + * eagerly loading the entire contents into PHP memory. For streams, however, + * it’s more important to avoid validating a separate copy of the file data + * than is later fetched by Imagick, so go ahead and load the entire payload, + * then pass it to Imagick as the data blob itself. + * + * @link https://mimesniff.spec.whatwg.org/#reading-the-resource-header + */ + try { + if ( $is_file ) { + $file_data = file_get_contents( $this->file, false, null, 0, 1445 ); + } else { + $file_data = file_get_contents( $this->file ); + } + } catch ( Exception $e ) { + $file_data = false; + } + if ( false === $file_data ) { + return new WP_Error( 'error_loading_image', __( 'File does not exist?' ), $this->file ); + } + + $pdf_extensions = array( + 'ai', + 'epdf', + 'pdf', + 'pdfa', + 'pocketmod', + ); + + // Reject files claiming to be PDFs which lack the required signature. + $has_pdf_extension = in_array( $file_extension, $pdf_extensions, true ); + $has_pdf_signature = str_starts_with( $file_data, '%PDF-' ); + if ( $has_pdf_extension && ! $has_pdf_signature ) { + return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file ); + } + + $ps_formats = array( + 'DPS', + 'EPI', + 'EPS', + 'EPSF', + 'EPSI', + 'PS', + 'WPG', + ); + + $ps_extensions = array( + 'dps', + 'epi', + 'eps', + 'eps2', + 'eps3', + 'epsf', + 'epsi', + 'ept', + 'ept2', + 'ept3', + 'ps', + 'ps2', + 'ps3', + 'wpg', + ); + + // Reject files which Imagick will parse as PostScript. + if ( + array() !== array_intersect( $imagick_formats, $ps_formats ) || + in_array( $file_extension, $ps_extensions, true ) || + str_starts_with( $file_data, '%!' ) || + str_starts_with( $file_data, "\x04%!" ) || + str_starts_with( $file_data, "\xC5\xD0\xD3\xC6" ) || + str_starts_with( $file_data, "\xFFWPC" ) + ) { + return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file ); + } + + $compressed_extensions = array( + 'gz', + 'bz2', + 'svgz', + 'z', + 'wmz', + ); + + /* + * Reject compressed archives that Imagick will transparently decompress. + * Unfortunately this rejects `.svgz` because there’s no intermediate step + * in the loading process. `Imagick` would decompress the file, then look + * to see what kind of content was decompressed instead of asserting SVG. + */ + if ( + in_array( $file_extension, $compressed_extensions, true ) || + str_starts_with( $file_data, "\x1F\x8B\x08" ) || // gzip + str_starts_with( $file_data, 'BZh' ) || // bzip2 + str_starts_with( $file_data, "\x1F\x9D" ) // compress + ) { + return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file ); + } + try { - $this->image = new Imagick(); - $file_extension = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) ); + $this->image = new Imagick(); - if ( 'pdf' === $file_extension ) { - $pdf_loaded = $this->pdf_load_source(); + if ( $has_pdf_signature ) { + /* + * Load these values for use in the helper method without forcing a change + * of its expected arguments, but then free them after calling to prevent + * keeping them around in memory and bloating the app. + */ + $this->stream_file_data = $is_stream ? $file_data : null; + $this->image_given_name = $given_filename; + $pdf_loaded = $this->pdf_load_source(); + $this->stream_file_data = null; + $this->image_given_name = null; if ( is_wp_error( $pdf_loaded ) ) { return $pdf_loaded; } } else { - if ( wp_is_stream( $this->file ) ) { - // Due to reports of issues with streams with `Imagick::readImageFile()`, uses `Imagick::readImageBlob()` instead. - $this->image->readImageBlob( file_get_contents( $this->file ), $this->file ); + if ( $is_stream ) { + $this->image->readImageBlob( $file_data, $given_filename ); } else { $this->image->readImage( $this->file ); } @@ -168,7 +368,7 @@ $this->image->setIteratorIndex( 0 ); } - if ( 'pdf' === $file_extension ) { + if ( $has_pdf_signature ) { $this->remove_pdf_alpha_channel(); } @@ -1110,25 +1310,35 @@ return $filename; } - try { - /* - * When generating thumbnails from cropped PDF pages, Imagemagick uses the uncropped - * area (resulting in unnecessary whitespace) unless the following option is set. - */ - $this->image->setOption( 'pdf:use-cropbox', true ); + foreach ( array( 'true', 'false' ) as $use_cropbox ) { + try { + /** + * When generating thumbnails from cropped PDF pages, Imagemagick uses the uncropped + * area (resulting in unnecessary whitespace) unless the following option is set. + * + * However, it sometimes fails, so if that happens, run it without the option. + * + * @ticket 48853 + */ + $this->image->setOption( 'pdf:use-cropbox', $use_cropbox ); - /* - * Reading image after Imagick instantiation because `setResolution` - * only applies correctly before the image is read. - */ - $this->image->readImage( $filename ); - } catch ( Exception $e ) { - // Attempt to run `gs` without the `use-cropbox` option. See #48853. - $this->image->setOption( 'pdf:use-cropbox', false ); + /* + * Reading image after Imagick instantiation because `setResolution` + * only applies correctly before the image is read. + */ + if ( is_string( $this->stream_file_data ) ) { + $this->image->setFilename( 'PDF:unknown.pdf[0]' ); + $this->image->readImageBlob( $this->stream_file_data, $this->image_given_name ); + } else { + $this->image->readImage( $filename ); + } - $this->image->readImage( $filename ); + return true; + } catch ( Exception $e ) { + continue; + } } - return true; + return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file ); } } diff -Nru wordpress-6.8.7+dfsg1/wp-includes/class-wp-xmlrpc-server.php wordpress-6.8.10+dfsg1/wp-includes/class-wp-xmlrpc-server.php --- wordpress-6.8.7+dfsg1/wp-includes/class-wp-xmlrpc-server.php 2025-02-08 16:00:19.045116000 +0000 +++ wordpress-6.8.10+dfsg1/wp-includes/class-wp-xmlrpc-server.php 2026-09-17 18:21:54.640900000 +0000 @@ -1454,6 +1454,33 @@ return new IXR_Error( 403, __( 'Invalid post type.' ) ); } + // Reject writes to internal-only builtin post types (e.g. customize_changeset) + // whose intended write path is a dedicated helper, not a generic post API. + $is_internal_only = ( + empty( $post_type->public ) + && empty( $post_type->show_in_rest ) + && ! empty( $post_type->_builtin ) + ); + + /** + * Filters whether a post type accepts writes via XML-RPC. + * + * Defaults to false for internal-only builtin post types (public=false, + * show_in_rest=false, _builtin=true), such as customize_changeset, whose + * writes are meant to flow through dedicated helpers. Return true to opt + * a post type back in. + * + * @since 7.1.1 + * + * @param bool $allowed Whether the post type accepts XML-RPC writes. + * @param WP_Post_Type $post_type The post type object. + */ + $allowed = apply_filters( 'xmlrpc_allow_post_type_writes', ! $is_internal_only, $post_type ); + + if ( ! $allowed ) { + return new IXR_Error( 403, __( 'Sorry, this post type is not supported over XML-RPC.' ) ); + } + $update = ! empty( $post_data['ID'] ); if ( $update ) { diff -Nru wordpress-6.8.7+dfsg1/wp-includes/customize/class-wp-customize-header-image-setting.php wordpress-6.8.10+dfsg1/wp-includes/customize/class-wp-customize-header-image-setting.php --- wordpress-6.8.7+dfsg1/wp-includes/customize/class-wp-customize-header-image-setting.php 2023-07-10 23:00:22.772857000 +0000 +++ wordpress-6.8.10+dfsg1/wp-includes/customize/class-wp-customize-header-image-setting.php 2026-09-17 18:21:54.640900000 +0000 @@ -15,6 +15,17 @@ * @since 3.4.0 * * @see WP_Customize_Setting + * + * @phpstan-type Header_Image_Data array{ + * attachment_id?: int, + * url?: string, + * thumbnail_url?: string, + * timestamp?: int, + * width?: int, + * height?: int, + * alt_text?: string, + * attachment_parent?: int, + * } */ final class WP_Customize_Header_Image_Setting extends WP_Customize_Setting { @@ -59,4 +70,163 @@ $custom_image_header->set_header_image( $value ); } } + + /** + * Sanitizes a header value. + * + * The value is expected to be one of the following: + * + * - An array of header image data, with the keys `attachment_id`, `url`, `thumbnail_url`, `timestamp`, `width`, + * `height`, `alt_text`, and `attachment_parent`, as supplied by {@see get_uploaded_header_images()}. Any other + * key is discarded. + * - An array with a `choice` key, being the legacy format in which any of the other accepted values is nested. + * - The string `remove-header`, `random-default-image`, or `random-uploaded-image`. + * - A string corresponding to one of the keys for the array returned by {@see get_uploaded_header_images()}, or + * one of the keys for the array passed into {@see register_default_headers()}. + * + * @since 7.1.1 + * + * @see WP_Customize_Header_Image_Setting::update() + * @see Custom_Image_Header::set_header_image() + * + * @param mixed $value Value to sanitize. + * @return array|string|WP_Error|null Sanitized value, or `null`/`WP_Error` if invalid. The array holds + * the header image data, or that data nested under a `choice` key, + * before the `customize_sanitize_header_image_data` filter, which + * may return anything, is applied to it. + * + * @phpstan-return array|string|WP_Error|null + */ + public function sanitize( $value ) { + /* + * The update() method unwraps the legacy `choice` format before handing the value off to + * Custom_Image_Header::set_header_image(), so the nested value is what must be sanitized. + */ + if ( is_array( $value ) && isset( $value['choice'] ) ) { + $choice = $this->sanitize_choice( $value['choice'] ); + if ( is_null( $choice ) || is_wp_error( $choice ) ) { + return $choice; + } + $value = array( 'choice' => $choice ); + } else { + $value = $this->sanitize_choice( $value ); + if ( is_null( $value ) || is_wp_error( $value ) ) { + return $value; + } + } + + return parent::sanitize( $value ); + } + + /** + * Sanitizes a header image choice. + * + * This is the value which is ultimately passed to {@see Custom_Image_Header::set_header_image()}, whether + * supplied at the top level of the setting value or nested under its legacy `choice` key. + * + * @since 7.1.1 + * + * @param mixed $value Value to sanitize. + * @return array|string|WP_Error|null Sanitized value, or `null`/`WP_Error` if invalid. + * + * @phpstan-return Header_Image_Data|string|WP_Error|null + */ + private function sanitize_choice( $value ) { + // Custom_Image_Header::set_header_image() accepts an object in place of an array. + if ( is_object( $value ) ) { + $value = (array) $value; + } + + if ( is_string( $value ) ) { + return sanitize_text_field( $value ); + } + + if ( ! is_array( $value ) ) { + return null; + } + + /* + * The sanitized value is assembled member by member rather than filtered down from the + * supplied one, so that nothing but the members below can end up in it. + */ + $sanitized = array(); + + if ( isset( $value['attachment_id'] ) ) { + if ( ! is_scalar( $value['attachment_id'] ) ) { + return null; + } + $attachment_id = absint( $value['attachment_id'] ); + + /* + * A supplied attachment must be an existing image, since its ID is written to postmeta and its + * data displayed. Note that an ID of zero must be skipped rather than looked up, as + * get_post_mime_type() falls back to the global post when passed an empty value. + */ + if ( $attachment_id > 0 ) { + $mime_type = get_post_mime_type( $attachment_id ); + if ( ! is_string( $mime_type ) || ! str_starts_with( $mime_type, 'image/' ) ) { + return null; + } + } + + $sanitized['attachment_id'] = $attachment_id; + } + + if ( isset( $value['url'] ) ) { + if ( ! is_string( $value['url'] ) ) { + return null; + } + $sanitized['url'] = sanitize_url( $value['url'] ); + if ( '' === $sanitized['url'] ) { + return new WP_Error( 'invalid_url', __( 'Invalid URL.' ) ); + } + } + + if ( isset( $value['thumbnail_url'] ) ) { + if ( ! is_string( $value['thumbnail_url'] ) ) { + return null; + } + $sanitized['thumbnail_url'] = sanitize_url( $value['thumbnail_url'] ); + if ( '' === $sanitized['thumbnail_url'] ) { + return new WP_Error( 'invalid_url', __( 'Invalid URL.' ) ); + } + } + + if ( isset( $value['timestamp'] ) ) { + if ( ! is_scalar( $value['timestamp'] ) ) { + return null; + } + $sanitized['timestamp'] = absint( $value['timestamp'] ); + } + + if ( isset( $value['width'] ) ) { + if ( ! is_scalar( $value['width'] ) ) { + return null; + } + $sanitized['width'] = absint( $value['width'] ); + } + + if ( isset( $value['height'] ) ) { + if ( ! is_scalar( $value['height'] ) ) { + return null; + } + $sanitized['height'] = absint( $value['height'] ); + } + + if ( isset( $value['alt_text'] ) ) { + if ( ! is_string( $value['alt_text'] ) ) { + return null; + } + $sanitized['alt_text'] = sanitize_text_field( $value['alt_text'] ); + } + + if ( isset( $value['attachment_parent'] ) ) { + if ( ! is_scalar( $value['attachment_parent'] ) ) { + return null; + } + $sanitized['attachment_parent'] = absint( $value['attachment_parent'] ); + } + + return $sanitized; + } } diff -Nru wordpress-6.8.7+dfsg1/wp-includes/formatting.php wordpress-6.8.10+dfsg1/wp-includes/formatting.php --- wordpress-6.8.7+dfsg1/wp-includes/formatting.php 2025-07-05 06:35:32.151945000 +0000 +++ wordpress-6.8.10+dfsg1/wp-includes/formatting.php 2026-09-17 18:21:54.640900000 +0000 @@ -560,7 +560,7 @@ $text = preg_replace( '|

(|', '$1', $text ); // If a

is wrapped with a

, move it inside the

. - $text = preg_replace( '|

]*)>|i', '

', $text ); + $text = preg_replace( '!

"\']|"[^"]*"|\'[^\']*\')*)>!i', '

', $text ); $text = str_replace( '

', '

', $text ); // If an opening or closing block element tag is preceded by an opening

tag, remove it. diff -Nru wordpress-6.8.7+dfsg1/wp-includes/html-api/class-wp-html-tag-processor.php wordpress-6.8.10+dfsg1/wp-includes/html-api/class-wp-html-tag-processor.php --- wordpress-6.8.7+dfsg1/wp-includes/html-api/class-wp-html-tag-processor.php 2026-03-10 16:37:37.009287000 +0000 +++ wordpress-6.8.10+dfsg1/wp-includes/html-api/class-wp-html-tag-processor.php 2026-09-17 18:21:54.640900000 +0000 @@ -3706,7 +3706,7 @@ self::COMMENT_AS_HTML_COMMENT === $this->comment_type ) { // Check if the text could close the comment. - if ( 1 === preg_match( '/--!?>/', $plaintext_content ) ) { + if ( 1 === preg_match( '/^-?>|--!?>/', $plaintext_content ) ) { return false; } diff -Nru wordpress-6.8.7+dfsg1/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php wordpress-6.8.10+dfsg1/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php --- wordpress-6.8.7+dfsg1/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php 2025-03-11 14:19:20.051252000 +0000 +++ wordpress-6.8.10+dfsg1/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php 2026-09-17 18:21:54.640900000 +0000 @@ -781,6 +781,7 @@ * Checks if a given REST request has access to update a comment. * * @since 4.7.0 + * @since 7.1.0 Target post permissions are checked when a comment's parent post is changed. * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has access to update the item, error object otherwise. @@ -799,6 +800,22 @@ ); } + /* + * check_edit_permission() above only establishes that the comment may be + * edited where it currently sits, because 'edit_comment' maps to 'edit_post' + * on the comment's current parent. When the parent is being changed, the new + * parent has to be authorized as well. Without this, a user holding + * edit_comment on their own comment could reparent it onto any post, + * including posts they can neither read nor edit. + */ + if ( isset( $request['post'] ) && (int) $request['post'] !== (int) $comment->comment_post_ID ) { + $target_check = $this->check_target_post_permission( (int) $request['post'] ); + + if ( is_wp_error( $target_check ) ) { + return $target_check; + } + } + return true; } @@ -1928,4 +1945,58 @@ */ return '' !== $check['comment_content']; } + + /** + * Checks that a post can receive a comment from the current user. + * + * Used when changing the parent post of an existing comment, so that + * attaching a comment to a post is authorized the same way whichever + * path it arrives by. + * + * @since 7.1.0 + * + * @param int $post_id Target post ID. + * @return true|WP_Error True if the post can receive the comment, error object otherwise. + */ + protected function check_target_post_permission( $post_id ) { + if ( ! $post_id ) { + return new WP_Error( + 'rest_comment_invalid_post_id', + __( 'Sorry, you are not allowed to create this comment without a post.' ), + array( 'status' => 403 ) + ); + } + + /* + * A comment needs either comment moderation rights or edit access to the + * post, which is what check_edit_permission() grants on the post a comment + * is moving away from. Requiring the same at the destination means both + * ends of a move are authorized alike. + */ + if ( ! current_user_can( 'moderate_comments' ) && ! current_user_can( 'edit_post', $post_id ) ) { + return new WP_Error( + 'rest_cannot_edit', + __( 'Sorry, you are not allowed to edit this comment.' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + $post = get_post( $post_id ); + + if ( ! $post ) { + return new WP_Error( + 'rest_comment_invalid_post_id', + __( 'Sorry, you are not allowed to create this comment without a post.' ), + array( 'status' => 403 ) + ); + } + + /* + * The create-time draft and comments-open rules are deliberately not applied + * here, because moderators move comments onto posts whose discussion has + * closed and onto drafts today. Enforcing them would break that without + * blocking anything the capability check above already permits. + */ + return true; + } } diff -Nru wordpress-6.8.7+dfsg1/wp-includes/template.php wordpress-6.8.10+dfsg1/wp-includes/template.php --- wordpress-6.8.7+dfsg1/wp-includes/template.php 2024-02-21 19:26:08.320857000 +0000 +++ wordpress-6.8.10+dfsg1/wp-includes/template.php 2026-09-22 12:43:47.816954000 +0000 @@ -486,7 +486,7 @@ } if ( $pagename ) { $pagename_decoded = urldecode( $pagename ); - if ( $pagename_decoded !== $pagename ) { + if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) { $templates[] = "page-{$pagename_decoded}.php"; } $templates[] = "page-{$pagename}.php"; @@ -695,6 +695,67 @@ } /** + * Determines whether a template found by locate_template() may be loaded. + * + * @since 7.1.2 + * @access private + * + * @global string $wp_stylesheet_path Path to current theme's stylesheet directory. + * @global string $wp_template_path Path to current theme's template directory. + * + * @param string $path Path to an existing template file. + * @return bool Whether the template may be loaded. + */ +function _wp_is_template_path_allowed( $path ) { + global $wp_stylesheet_path, $wp_template_path; + + // A file path that exists and does not contain `..` is allowed. + if ( 0 === preg_match( '#(?:^|/)\.\.[. ]*(?:/|$)#', wp_normalize_path( $path ) ) ) { + return true; + } + + // Resolve the true location of the requested file for later comparison. + $real_path = realpath( $path ); + + if ( false === $real_path ) { + return false; + } + + $real_path = trailingslashit( wp_normalize_path( $real_path ) ); + + $directories = array( + $wp_stylesheet_path, + $wp_template_path, + ABSPATH . WPINC . '/theme-compat', + ); + + // If a theme is in a subdirectory, accept templates from its direct parent directory. + if ( str_contains( get_stylesheet(), '/' ) ) { + $directories[] = dirname( $wp_stylesheet_path ); + } + + // If a parent theme is in a subdirectory, accept templates from its direct parent directory. + if ( str_contains( get_template(), '/' ) ) { + $directories[] = dirname( $wp_template_path ); + } + + foreach ( $directories as $directory ) { + $real_directory = realpath( $directory ); + + if ( false === $real_directory ) { + continue; + } + + // The true location of the requested file must be inside one of the allowed directories. + if ( str_starts_with( $real_path, trailingslashit( wp_normalize_path( $real_directory ) ) ) ) { + return true; + } + } + + return false; +} + +/** * Retrieves the name of the highest priority template file that exists. * * Searches in the stylesheet directory before the template directory and @@ -703,6 +764,7 @@ * * @since 2.7.0 * @since 5.5.0 The `$args` parameter was added. + * @since 7.1.2 A template name containing `..` is only located if it resolves inside the theme. * * @global string $wp_stylesheet_path Path to current theme's stylesheet directory. * @global string $wp_template_path Path to current theme's template directory. @@ -730,13 +792,17 @@ continue; } if ( file_exists( $wp_stylesheet_path . '/' . $template_name ) ) { - $located = $wp_stylesheet_path . '/' . $template_name; - break; + $candidate = $wp_stylesheet_path . '/' . $template_name; } elseif ( $is_child_theme && file_exists( $wp_template_path . '/' . $template_name ) ) { - $located = $wp_template_path . '/' . $template_name; - break; + $candidate = $wp_template_path . '/' . $template_name; } elseif ( file_exists( ABSPATH . WPINC . '/theme-compat/' . $template_name ) ) { - $located = ABSPATH . WPINC . '/theme-compat/' . $template_name; + $candidate = ABSPATH . WPINC . '/theme-compat/' . $template_name; + } else { + continue; + } + + if ( _wp_is_template_path_allowed( $candidate ) ) { + $located = $candidate; break; } } diff -Nru wordpress-6.8.7+dfsg1/wp-includes/theme.php wordpress-6.8.10+dfsg1/wp-includes/theme.php --- wordpress-6.8.7+dfsg1/wp-includes/theme.php 2025-03-18 12:30:25.301830000 +0000 +++ wordpress-6.8.10+dfsg1/wp-includes/theme.php 2026-09-17 18:21:54.640900000 +0000 @@ -1547,6 +1547,7 @@ * Gets the header image data. * * @since 3.4.0 + * @since 7.1.1 The `width` and `height` are cast to non-negative integers. * * @global array $_wp_default_headers * @@ -1585,7 +1586,14 @@ 'height' => get_theme_support( 'custom-header', 'height' ), 'video' => get_theme_support( 'custom-header', 'video' ), ); - return (object) wp_parse_args( $data, $default ); + + if ( ! is_array( $data ) && ! is_object( $data ) ) { + $data = array(); + } + $header = (object) wp_parse_args( $data, $default ); + $header->width = absint( $header->width ); + $header->height = absint( $header->height ); + return $header; } /** diff -Nru wordpress-6.8.7+dfsg1/wp-includes/version.php wordpress-6.8.10+dfsg1/wp-includes/version.php --- wordpress-6.8.7+dfsg1/wp-includes/version.php 2026-08-06 18:10:34.000000000 +0000 +++ wordpress-6.8.10+dfsg1/wp-includes/version.php 2026-09-22 13:00:22.000000000 +0000 @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.8.7'; +$wp_version = '6.8.10'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.