From 53a5389f77dc381df5dc37ea6a3b3497d6f8decf Mon Sep 17 00:00:00 2001 From: henry-idingo Date: Thu, 6 Aug 2026 15:26:37 +0200 Subject: [PATCH] Clear the Plugin Check findings worth clearing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two of the four are real. load_plugin_textdomain() has been unnecessary since WordPress 4.6 for a plugin hosted on WordPress.org — translations arrive keyed by the slug, which is what the text domain is now. The call was also pointing at a languages/ directory this plugin does not ship. Removed. REQUEST_URI was read without a sanitizer in two places. The reflex fix is sanitize_text_field(), and it would have been a bug: it loops deleting every %xx sequence, so /caf%C3%A9/ reaches CiteCue as /caf/ — a different page, cached under a key CiteCue never answers for. esc_url_raw() is the sanitizer for a URL and leaves the encoding intact. A test pins this down; it was confirmed to fail against sanitize_text_field() before being kept. The other two findings stay as they are. DONOTCACHEPAGE is deliberately unprefixed — page-cache plugins look for that exact name, so prefixing it would defeat the point, and .phpcs.xml.dist has said so since before this change. The readme's "Tested up to" error was already fixed in 1.0.1; the report was generated against an older install, still in a citecue/ folder. --- citecue.php | 4 ++-- includes/class-citecue-llms-txt.php | 4 +++- includes/class-citecue-plugin.php | 9 ++++++--- includes/class-citecue-proxy.php | 5 ++++- readme.txt | 6 +++++- tests/cases/test-proxy-delivery.php | 21 +++++++++++++++++++++ 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/citecue.php b/citecue.php index 0fbea71..b78e1f8 100644 --- a/citecue.php +++ b/citecue.php @@ -3,7 +3,7 @@ * Plugin Name: CiteCue AI Auto-Fix * Plugin URI: https://github.com/citecue/wordpress-plugin * Description: Serves CiteCue-optimized versions of your pages to AI bots and crawlers, publishes your llms.txt, and lets CiteCue push brand-building draft content into WordPress. - * Version: 1.0.1 + * Version: 1.0.2 * Requires at least: 5.8 * Requires PHP: 7.4 * Author: CiteCue @@ -56,7 +56,7 @@ static function () { return; } -define( 'CITECUE_VERSION', '1.0.1' ); +define( 'CITECUE_VERSION', '1.0.2' ); define( 'CITECUE_PLUGIN_FILE', __FILE__ ); define( 'CITECUE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); diff --git a/includes/class-citecue-llms-txt.php b/includes/class-citecue-llms-txt.php index 4b68d09..89af704 100644 --- a/includes/class-citecue-llms-txt.php +++ b/includes/class-citecue-llms-txt.php @@ -178,7 +178,9 @@ private function is_llms_txt_request() { return false; } - $path = (string) wp_parse_url( wp_unslash( $_SERVER['REQUEST_URI'] ), PHP_URL_PATH ); + // esc_url_raw() rather than sanitize_text_field(), which strips + // percent-encoded sequences and would corrupt the path being matched. + $path = (string) wp_parse_url( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ), PHP_URL_PATH ); $home_path = (string) wp_parse_url( home_url( '/' ), PHP_URL_PATH ); $target = untrailingslashit( $home_path ) . '/llms.txt'; diff --git a/includes/class-citecue-plugin.php b/includes/class-citecue-plugin.php index 1761897..fc4f658 100644 --- a/includes/class-citecue-plugin.php +++ b/includes/class-citecue-plugin.php @@ -102,13 +102,16 @@ private function __construct() { } /** - * Init: translations + cron self-heal. + * Init: cron self-heal. + * + * There is deliberately no load_plugin_textdomain() call. Since WordPress + * 4.6 a plugin hosted on WordPress.org has its translations loaded for it, + * keyed by the slug — which is exactly what the text domain is now. The + * call was pointing at a languages/ directory this plugin does not ship. * * @return void */ public function on_init() { - load_plugin_textdomain( 'citecue-ai-auto-fix', false, dirname( plugin_basename( CITECUE_PLUGIN_FILE ) ) . '/languages' ); - if ( ! wp_next_scheduled( self::CRON_HOOK ) ) { wp_schedule_event( time() + MINUTE_IN_SECONDS, 'daily', self::CRON_HOOK ); } diff --git a/includes/class-citecue-proxy.php b/includes/class-citecue-proxy.php index cec994b..c77ec3b 100644 --- a/includes/class-citecue-proxy.php +++ b/includes/class-citecue-proxy.php @@ -308,7 +308,10 @@ private function current_url() { } $scheme = is_ssl() ? 'https' : 'http'; $host = sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ); - $uri = wp_unslash( $_SERVER['REQUEST_URI'] ); + // esc_url_raw(), not sanitize_text_field(): the latter deletes every + // percent-encoded sequence it finds, so /caf%C3%A9/ would reach CiteCue + // as /caf/ and be cached under the wrong key. + $uri = esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ); return esc_url_raw( $scheme . '://' . $host . $uri ); } diff --git a/readme.txt b/readme.txt index 9ce22d4..609cde8 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: ai, llms.txt, gptbot, ai-seo, woocommerce Requires at least: 5.8 Tested up to: 7.0 Requires PHP: 7.4 -Stable tag: 1.0.1 +Stable tag: 1.0.2 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -100,6 +100,10 @@ The plugin folder is now citecue-ai-auto-fix. If you installed 1.0.0 by uploadin == Changelog == += 1.0.2 = +* Translations no longer depend on the plugin loading them by hand; WordPress.org supplies them for the plugin slug. +* The requested URL is sanitized on the way to CiteCue in a way that preserves percent-encoding, so pages with accented or non-Latin characters in the address are served and cached under the address they actually have. + = 1.0.1 = * First release in the WordPress.org plugin directory. * An install that has not been connected to CiteCue now makes no outbound requests of any kind: the daily crawler-registry refresh waits for a connection. diff --git a/tests/cases/test-proxy-delivery.php b/tests/cases/test-proxy-delivery.php index 703c215..1851a12 100644 --- a/tests/cases/test-proxy-delivery.php +++ b/tests/cases/test-proxy-delivery.php @@ -75,6 +75,27 @@ public function test_delivery_request_is_addressed_correctly() { $this->assertSame( 'wordpress', $request['args']['headers']['X-Citecue-Channel'] ); } + /** + * The requested URL reaches CiteCue with its percent-encoding intact. + * + * This is a standing trap rather than a hypothetical: REQUEST_URI has to + * be sanitized to satisfy Plugin Check, and the reflex choice — + * sanitize_text_field() — deletes every %xx sequence it finds. Under it + * this URL would arrive as /caf/, which is a different page, cached under + * a key CiteCue never answers for. + * + * @return void + */ + public function test_a_percent_encoded_url_is_not_mangled_on_the_way_out() { + $encoded = $this->fake_crawler_request( '/caf%C3%A9/' ); + $this->http->queue( 'page', 200, 'optimized' ); + + $this->proxy()->decide(); + + $this->assertStringContainsString( '%C3%A9', rawurldecode( $this->http->last( 'page' )['url'] ) ); + $this->assertStringContainsString( 'caf%C3%A9', $encoded ); + } + /** * A second hit revalidates with the stored ETag instead of re-downloading. *