Changeset 3763844
- Timestamp:
- 10/25/2026 12:38:56 AM (16 hours ago)
- Location:
- gen-wave
- Files:
-
- 18 edited
- 1 copied
-
tags/1.2.2 (copied) (copied from gen-wave/trunk )
-
tags/1.2.2/gen-wave.php (modified) (2 diffs)
-
tags/1.2.2/languages/gen-wave.pot (modified) (5 diffs)
-
tags/1.2.2/readme.txt (modified) (2 diffs)
-
tags/1.2.2/src/AjaxManager.php (modified) (2 diffs)
-
tags/1.2.2/src/Core/AgentAuth.php (modified) (5 diffs)
-
tags/1.2.2/src/Core/AiRestApi.php (modified) (4 diffs)
-
tags/1.2.2/src/Handlers/GenerationHandler.php (modified) (1 diff)
-
tags/1.2.2/src/Handlers/ProductHandler.php (modified) (1 diff)
-
tags/1.2.2/vendor/composer/installed.php (modified) (2 diffs)
-
trunk/gen-wave.php (modified) (2 diffs)
-
trunk/languages/gen-wave.pot (modified) (5 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/src/AjaxManager.php (modified) (2 diffs)
-
trunk/src/Core/AgentAuth.php (modified) (5 diffs)
-
trunk/src/Core/AiRestApi.php (modified) (4 diffs)
-
trunk/src/Handlers/GenerationHandler.php (modified) (1 diff)
-
trunk/src/Handlers/ProductHandler.php (modified) (1 diff)
-
trunk/vendor/composer/installed.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
gen-wave/tags/1.2.2/gen-wave.php
r3724835 r3763844 3 3 * Plugin Name: Genwave - AI Agent 4 4 * Description: Run the WordPress site you already have by describing what you need: pages, WooCommerce, plugins, SEO and error fixes. Genwave Agent shows you the plan, you approve it, and it does the work. 5 * Version: 1.2. 15 * Version: 1.2.2 6 6 * Author: Genwave.ai 7 7 * Author URI: https://genwave.ai … … 29 29 } 30 30 31 define( 'GEN_WAVE_VERSION', '1.2. 1' );31 define( 'GEN_WAVE_VERSION', '1.2.2' ); 32 32 33 33 define( 'GEN_WAVE__FILE__', __FILE__ ); -
gen-wave/tags/1.2.2/languages/gen-wave.pot
r3724835 r3763844 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Genwave - AI Agent 1.2. 0\n"5 "Project-Id-Version: Genwave - AI Agent 1.2.1\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/gen-wave\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2026-10- 02T15:04:44+02:00\n"12 "POT-Creation-Date: 2026-10-25T02:28:21+02:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 53 53 msgstr "" 54 54 55 #: src/AjaxManager.php:98 355 #: src/AjaxManager.php:980 56 56 #: src/Handlers/GenerationHandler.php:351 57 57 #: src/Handlers/PollingHandler.php:41 … … 63 63 64 64 #: src/Controllers/VerifyLoginController.php:26 65 #: src/Core/AgentAuth.php: 4465 #: src/Core/AgentAuth.php:66 66 66 msgid "Enter your license key first." 67 67 msgstr "" 68 68 69 #: src/Core/AgentAuth.php: 6469 #: src/Core/AgentAuth.php:90 70 70 #: src/Core/PanelConnect.php:157 71 71 msgid "Connection failed." 72 72 msgstr "" 73 73 74 #: src/Core/AgentAuth.php: 8174 #: src/Core/AgentAuth.php:107 75 75 msgid "Your site is connected." 76 76 msgstr "" 77 77 78 #: src/Core/AgentAuth.php:1 0778 #: src/Core/AgentAuth.php:135 79 79 msgid "Your site has been disconnected." 80 80 msgstr "" 81 81 82 #: src/Core/AgentAuth.php:1 2982 #: src/Core/AgentAuth.php:157 83 83 msgid "Genwave needs to reconnect your site to keep working securely." 84 84 msgstr "" 85 85 86 #: src/Core/AgentAuth.php:1 3086 #: src/Core/AgentAuth.php:158 87 87 msgid "Reconnect now" 88 88 msgstr "" 89 89 90 #: src/Core/AiRestApi.php:1 0491 #: src/Core/AiRestApi.php:1 2190 #: src/Core/AiRestApi.php:125 91 #: src/Core/AiRestApi.php:142 92 92 msgid "You do not have permission to access this endpoint" 93 93 msgstr "" 94 94 95 #: src/Core/AiRestApi.php:2 3195 #: src/Core/AiRestApi.php:252 96 96 msgid "Response data saved successfully" 97 97 msgstr "" 98 98 99 #: src/Core/AiRestApi.php:2 3399 #: src/Core/AiRestApi.php:254 100 100 msgid "Invalid JSON in response" 101 101 msgstr "" 102 102 103 #: src/Core/AiRestApi.php:2 36103 #: src/Core/AiRestApi.php:257 104 104 msgid "Missing response key in data" 105 105 msgstr "" 106 106 107 #: src/Core/AiRestApi.php:2 40107 #: src/Core/AiRestApi.php:261 108 108 msgid "No data received" 109 109 msgstr "" 110 110 111 #: src/Core/AiRestApi.php:456 111 #: src/Core/AiRestApi.php:480 112 msgid "This site is not connecting right now. Connect it from its Genwave settings." 113 msgstr "" 114 115 #: src/Core/AiRestApi.php:487 112 116 msgid "Plugin is not configured with a license key" 113 117 msgstr "" 114 118 115 #: src/Core/AiRestApi.php:462 116 #: src/Core/AiRestApi.php:482 119 #: src/Core/AiRestApi.php:493 117 120 msgid "Genwave plugin is active" 118 121 msgstr "" 119 122 120 #: src/Core/AiRestApi.php: 473121 msgid " Verification tokenis required"123 #: src/Core/AiRestApi.php:505 124 msgid "A verification challenge is required" 122 125 msgstr "" 123 126 … … 307 310 msgstr "" 308 311 312 #: src/Handlers/ProductHandler.php:254 313 msgid "You do not have permission to read this post." 314 msgstr "" 315 309 316 #: src/MetaBox.php:33 310 317 #: src/MetaBox.php:44 -
gen-wave/tags/1.2.2/readme.txt
r3724835 r3763844 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1.2. 17 Stable tag: 1.2.2 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 155 155 156 156 == Changelog == 157 158 = 1.2.2 = 159 * Security hardening for the REST API and site verification. 157 160 158 161 = 1.2.1 = -
gen-wave/tags/1.2.2/src/AjaxManager.php
r3714852 r3763844 51 51 add_action('wp_ajax_genwave_get_all_products', [$this->productHandler, 'getAllProducts']); 52 52 add_action('wp_ajax_genwave_get_all_posts', [$this->productHandler, 'getAllPosts']); 53 // Logged-in only (1.2.2): the handler checks edit_post for the post asked for. 53 54 add_action('wp_ajax_genwave_get_post_data', [$this->productHandler, 'handle_get_post_data']); 54 add_action('wp_ajax_nopriv_genwave_get_post_data', [$this->productHandler, 'handle_get_post_data']);55 55 56 56 add_action('wp_ajax_genwave_refresh_credits', [$this, 'handle_refresh_credits']); … … 65 65 add_action('wp_ajax_genwave_install_plugin', [$this->pluginsHandler, 'handle_install_plugin']); 66 66 67 // Polling handlers - delegated to PollingHandler 68 add_action('wp_ajax_genwave_polling_results', [$this->pollingHandler, 'handle_ai_polling_results']); 69 add_action('wp_ajax_nopriv_genwave_polling_results', [$this->pollingHandler, 'handle_ai_polling_results']); 70 add_action('wp_ajax_genwave_poll_proxy', [$this->pollingHandler, 'handle_ai_poll_proxy']); 71 add_action('wp_ajax_nopriv_genwave_poll_proxy', [$this->pollingHandler, 'handle_ai_poll_proxy']); 67 // REMOVED (1.2.2): genwave_polling_results and genwave_poll_proxy (also on 68 // nopriv). Only the unused streaming client called them. 72 69 // REMOVED (F11): the genwave_update_credit_balance / _token_balance actions 73 70 // and their handler are gone. Nothing called them (no JS caller anywhere), -
gen-wave/tags/1.2.2/src/Core/AgentAuth.php
r3642637 r3763844 28 28 } 29 29 30 /** 31 * The verify-domain challenge is answered only while this site is talking to 32 * the panel (connect, disconnect): a short window opened before the request 33 * and closed after it. Five minutes covers a slow panel; it closes itself. 34 */ 35 private const CHALLENGE_WINDOW = 'genwave_challenge_open'; 36 37 public static function openChallenge(): void 38 { 39 set_transient(self::CHALLENGE_WINDOW, 1, 5 * MINUTE_IN_SECONDS); 40 } 41 42 public static function closeChallenge(): void 43 { 44 delete_transient(self::CHALLENGE_WINDOW); 45 } 46 47 public static function challengeOpen(): bool 48 { 49 return (bool) get_transient(self::CHALLENGE_WINDOW); 50 } 51 30 52 /** True once the site has completed the signed connect. */ 31 53 public static function isConnected(): bool … … 45 67 } 46 68 69 // The panel calls back to verify-domain during this request: answer it now, and only now. 70 self::openChallenge(); 47 71 $response = wp_remote_post(self::panelUrl() . '/api/plugin/connect', [ 48 72 'headers' => ['Content-Type' => 'application/json', 'Accept' => 'application/json'], … … 56 80 ]); 57 81 82 self::closeChallenge(); 83 58 84 if (is_wp_error($response)) { 59 85 return ['success' => false, 'message' => $response->get_error_message()]; … … 93 119 94 120 if (!empty($licenseKey)) { 121 self::openChallenge(); 95 122 wp_remote_post(self::panelUrl() . '/api/plugin/disconnect', [ 96 123 'headers' => ['Content-Type' => 'application/json', 'Accept' => 'application/json'], … … 102 129 'sslverify' => ! preg_match('#(localhost|127\.0\.0\.1|\.local)#', self::panelUrl()), 103 130 ]); 131 self::closeChallenge(); 104 132 } 105 133 -
gen-wave/tags/1.2.2/src/Core/AiRestApi.php
r3714852 r3763844 50 50 * runs its own permission_callback afterwards, so nothing is actually loosened. 51 51 * 52 * Two things are never cleared (1.2.2): 53 * - core's own cookie check (`rest_cookie_invalid_nonce`): with a wrong nonce 54 * WordPress keeps the cookie user logged in and only this error stops the 55 * request, so clearing it turned any cookie request into a CSRF; 56 * - anything that is not our route. The route is the one WordPress parsed 57 * (`rest_route`), not a substring of the raw URI: a query string such as 58 * `/wp-json/wp/v2/users?x=/wp-json/gen-wave/v1/` used to count as ours. 59 * 52 60 * @param mixed $result null (no auth attempted), true (allowed) or WP_Error/false (denied). 53 61 * @return mixed … … 60 68 } 61 69 70 // Core's cookie authentication said no: that answer stands. 71 if (is_wp_error($result) && in_array('rest_cookie_invalid_nonce', $result->get_error_codes(), true)) { 72 return $result; 73 } 74 75 return $this->is_own_route($this->current_rest_route()) ? true : $result; 76 } 77 78 /** The REST route WordPress is serving (e.g. "/gen-wave/v1/verify-domain"), or ''. */ 79 private function current_rest_route(): string 80 { 81 if (isset($GLOBALS['wp']) && is_object($GLOBALS['wp']) && !empty($GLOBALS['wp']->query_vars['rest_route'])) { 82 return (string) $GLOBALS['wp']->query_vars['rest_route']; 83 } 62 84 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only route match, no state change 63 $uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : ''; 64 if (!is_string($uri) || $uri === '') { 65 return $result; 66 } 67 68 $prefix = rest_get_url_prefix(); // usually "wp-json" 69 $ns = $this->namespace; // "gen-wave/v1" 70 $ours = 71 strpos($uri, '/' . $prefix . '/' . $ns . '/') !== false || // pretty permalinks 72 strpos($uri, 'rest_route=/' . $ns . '/') !== false || // plain permalinks 73 strpos($uri, 'rest_route=' . rawurlencode('/' . $ns . '/')) !== false; // encoded 74 75 return $ours ? true : $result; 85 if (isset($_GET['rest_route']) && is_string($_GET['rest_route'])) { 86 // phpcs:ignore WordPress.Security.NonceVerification.Recommended 87 return sanitize_text_field(wp_unslash($_GET['rest_route'])); 88 } 89 return ''; 90 } 91 92 /** Whether a REST route is in this plugin's namespace (the route itself, never its query). */ 93 public function is_own_route(string $route): bool 94 { 95 $route = '/' . ltrim($route, '/'); 96 return strpos($route, '/' . $this->namespace . '/') === 0; 76 97 } 77 98 … … 449 470 450 471 // Secure challenge-response — proves domain control + key possession. 472 // Answered only while this site is itself connecting or disconnecting 473 // (AgentAuth::openChallenge, 1.2.2): otherwise anyone holding the license 474 // key could have the panel "connect" this site for them and receive its 475 // signing key. 451 476 if (!empty($challenge)) { 477 if (!AgentAuth::challengeOpen()) { 478 return new WP_REST_Response([ 479 'success' => false, 480 'message' => __('This site is not connecting right now. Connect it from its Genwave settings.', 'gen-wave') 481 ], 403); 482 } 452 483 $licenseKey = Config::get('license_key'); 453 484 if (empty($licenseKey)) { … … 467 498 } 468 499 469 // Legacy token echo (unchanged). 470 if (empty($token)) { 471 return new WP_REST_Response([ 472 'success' => false, 473 'message' => __('Verification token is required', 'gen-wave') 474 ], 400); 475 } 476 477 // NOTE (F10): the exact plugin version is intentionally NOT included — this 478 // endpoint is public (permission_callback __return_true), and leaking the 479 // precise version lets an unauthenticated caller fingerprint the site. 500 // The legacy token echo (retired account.genwave.ai flow) is gone (1.2.2): it 501 // repeated any token back, so anyone could "verify" any site with it. 502 unset($token); 480 503 return new WP_REST_Response([ 481 'success' => true, 482 'message' => __('Genwave plugin is active', 'gen-wave'), 483 'token' => $token, 484 'domain' => get_site_url(), 485 'verified_at' => current_time('mysql') 486 ], 200); 504 'success' => false, 505 'message' => __('A verification challenge is required', 'gen-wave') 506 ], 400); 487 507 } 488 508 } -
gen-wave/tags/1.2.2/src/Handlers/GenerationHandler.php
r3642637 r3763844 249 249 if (defined('WP_DEBUG') && WP_DEBUG) { 250 250 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug mode only 251 error_log("Genwave: License Key: " . $license_key);251 error_log("Genwave: Has License Key: " . ($license_key ? 'YES' : 'NO')); 252 252 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug mode only 253 253 error_log("Genwave: Has Token: " . ($encrypted_token ? 'YES' : 'NO')); -
gen-wave/tags/1.2.2/src/Handlers/ProductHandler.php
r3642637 r3763844 250 250 } 251 251 252 // A nonce proves where the request came from, not who may read the post (1.2.2). 253 if (!current_user_can('edit_post', $post_id)) { 254 wp_send_json_error(__('You do not have permission to read this post.', 'gen-wave'), 403); 255 return; 256 } 257 252 258 // Get the post 253 259 $post = get_post($post_id); -
gen-wave/tags/1.2.2/vendor/composer/installed.php
r3724835 r3763844 4 4 'pretty_version' => 'dev-master', 5 5 'version' => 'dev-master', 6 'reference' => ' 9f21dd8efa8e041d7d601e31c3813bd25a8ed4e6',6 'reference' => '8b75da2c8b8dc14d65d95387e3657e296bb2337f', 7 7 'type' => 'wordpress-plugin', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-master', 15 15 'version' => 'dev-master', 16 'reference' => ' 9f21dd8efa8e041d7d601e31c3813bd25a8ed4e6',16 'reference' => '8b75da2c8b8dc14d65d95387e3657e296bb2337f', 17 17 'type' => 'wordpress-plugin', 18 18 'install_path' => __DIR__ . '/../../', -
gen-wave/trunk/gen-wave.php
r3724835 r3763844 3 3 * Plugin Name: Genwave - AI Agent 4 4 * Description: Run the WordPress site you already have by describing what you need: pages, WooCommerce, plugins, SEO and error fixes. Genwave Agent shows you the plan, you approve it, and it does the work. 5 * Version: 1.2. 15 * Version: 1.2.2 6 6 * Author: Genwave.ai 7 7 * Author URI: https://genwave.ai … … 29 29 } 30 30 31 define( 'GEN_WAVE_VERSION', '1.2. 1' );31 define( 'GEN_WAVE_VERSION', '1.2.2' ); 32 32 33 33 define( 'GEN_WAVE__FILE__', __FILE__ ); -
gen-wave/trunk/languages/gen-wave.pot
r3724835 r3763844 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Genwave - AI Agent 1.2. 0\n"5 "Project-Id-Version: Genwave - AI Agent 1.2.1\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/gen-wave\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2026-10- 02T15:04:44+02:00\n"12 "POT-Creation-Date: 2026-10-25T02:28:21+02:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 53 53 msgstr "" 54 54 55 #: src/AjaxManager.php:98 355 #: src/AjaxManager.php:980 56 56 #: src/Handlers/GenerationHandler.php:351 57 57 #: src/Handlers/PollingHandler.php:41 … … 63 63 64 64 #: src/Controllers/VerifyLoginController.php:26 65 #: src/Core/AgentAuth.php: 4465 #: src/Core/AgentAuth.php:66 66 66 msgid "Enter your license key first." 67 67 msgstr "" 68 68 69 #: src/Core/AgentAuth.php: 6469 #: src/Core/AgentAuth.php:90 70 70 #: src/Core/PanelConnect.php:157 71 71 msgid "Connection failed." 72 72 msgstr "" 73 73 74 #: src/Core/AgentAuth.php: 8174 #: src/Core/AgentAuth.php:107 75 75 msgid "Your site is connected." 76 76 msgstr "" 77 77 78 #: src/Core/AgentAuth.php:1 0778 #: src/Core/AgentAuth.php:135 79 79 msgid "Your site has been disconnected." 80 80 msgstr "" 81 81 82 #: src/Core/AgentAuth.php:1 2982 #: src/Core/AgentAuth.php:157 83 83 msgid "Genwave needs to reconnect your site to keep working securely." 84 84 msgstr "" 85 85 86 #: src/Core/AgentAuth.php:1 3086 #: src/Core/AgentAuth.php:158 87 87 msgid "Reconnect now" 88 88 msgstr "" 89 89 90 #: src/Core/AiRestApi.php:1 0491 #: src/Core/AiRestApi.php:1 2190 #: src/Core/AiRestApi.php:125 91 #: src/Core/AiRestApi.php:142 92 92 msgid "You do not have permission to access this endpoint" 93 93 msgstr "" 94 94 95 #: src/Core/AiRestApi.php:2 3195 #: src/Core/AiRestApi.php:252 96 96 msgid "Response data saved successfully" 97 97 msgstr "" 98 98 99 #: src/Core/AiRestApi.php:2 3399 #: src/Core/AiRestApi.php:254 100 100 msgid "Invalid JSON in response" 101 101 msgstr "" 102 102 103 #: src/Core/AiRestApi.php:2 36103 #: src/Core/AiRestApi.php:257 104 104 msgid "Missing response key in data" 105 105 msgstr "" 106 106 107 #: src/Core/AiRestApi.php:2 40107 #: src/Core/AiRestApi.php:261 108 108 msgid "No data received" 109 109 msgstr "" 110 110 111 #: src/Core/AiRestApi.php:456 111 #: src/Core/AiRestApi.php:480 112 msgid "This site is not connecting right now. Connect it from its Genwave settings." 113 msgstr "" 114 115 #: src/Core/AiRestApi.php:487 112 116 msgid "Plugin is not configured with a license key" 113 117 msgstr "" 114 118 115 #: src/Core/AiRestApi.php:462 116 #: src/Core/AiRestApi.php:482 119 #: src/Core/AiRestApi.php:493 117 120 msgid "Genwave plugin is active" 118 121 msgstr "" 119 122 120 #: src/Core/AiRestApi.php: 473121 msgid " Verification tokenis required"123 #: src/Core/AiRestApi.php:505 124 msgid "A verification challenge is required" 122 125 msgstr "" 123 126 … … 307 310 msgstr "" 308 311 312 #: src/Handlers/ProductHandler.php:254 313 msgid "You do not have permission to read this post." 314 msgstr "" 315 309 316 #: src/MetaBox.php:33 310 317 #: src/MetaBox.php:44 -
gen-wave/trunk/readme.txt
r3724835 r3763844 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1.2. 17 Stable tag: 1.2.2 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 155 155 156 156 == Changelog == 157 158 = 1.2.2 = 159 * Security hardening for the REST API and site verification. 157 160 158 161 = 1.2.1 = -
gen-wave/trunk/src/AjaxManager.php
r3714852 r3763844 51 51 add_action('wp_ajax_genwave_get_all_products', [$this->productHandler, 'getAllProducts']); 52 52 add_action('wp_ajax_genwave_get_all_posts', [$this->productHandler, 'getAllPosts']); 53 // Logged-in only (1.2.2): the handler checks edit_post for the post asked for. 53 54 add_action('wp_ajax_genwave_get_post_data', [$this->productHandler, 'handle_get_post_data']); 54 add_action('wp_ajax_nopriv_genwave_get_post_data', [$this->productHandler, 'handle_get_post_data']);55 55 56 56 add_action('wp_ajax_genwave_refresh_credits', [$this, 'handle_refresh_credits']); … … 65 65 add_action('wp_ajax_genwave_install_plugin', [$this->pluginsHandler, 'handle_install_plugin']); 66 66 67 // Polling handlers - delegated to PollingHandler 68 add_action('wp_ajax_genwave_polling_results', [$this->pollingHandler, 'handle_ai_polling_results']); 69 add_action('wp_ajax_nopriv_genwave_polling_results', [$this->pollingHandler, 'handle_ai_polling_results']); 70 add_action('wp_ajax_genwave_poll_proxy', [$this->pollingHandler, 'handle_ai_poll_proxy']); 71 add_action('wp_ajax_nopriv_genwave_poll_proxy', [$this->pollingHandler, 'handle_ai_poll_proxy']); 67 // REMOVED (1.2.2): genwave_polling_results and genwave_poll_proxy (also on 68 // nopriv). Only the unused streaming client called them. 72 69 // REMOVED (F11): the genwave_update_credit_balance / _token_balance actions 73 70 // and their handler are gone. Nothing called them (no JS caller anywhere), -
gen-wave/trunk/src/Core/AgentAuth.php
r3642637 r3763844 28 28 } 29 29 30 /** 31 * The verify-domain challenge is answered only while this site is talking to 32 * the panel (connect, disconnect): a short window opened before the request 33 * and closed after it. Five minutes covers a slow panel; it closes itself. 34 */ 35 private const CHALLENGE_WINDOW = 'genwave_challenge_open'; 36 37 public static function openChallenge(): void 38 { 39 set_transient(self::CHALLENGE_WINDOW, 1, 5 * MINUTE_IN_SECONDS); 40 } 41 42 public static function closeChallenge(): void 43 { 44 delete_transient(self::CHALLENGE_WINDOW); 45 } 46 47 public static function challengeOpen(): bool 48 { 49 return (bool) get_transient(self::CHALLENGE_WINDOW); 50 } 51 30 52 /** True once the site has completed the signed connect. */ 31 53 public static function isConnected(): bool … … 45 67 } 46 68 69 // The panel calls back to verify-domain during this request: answer it now, and only now. 70 self::openChallenge(); 47 71 $response = wp_remote_post(self::panelUrl() . '/api/plugin/connect', [ 48 72 'headers' => ['Content-Type' => 'application/json', 'Accept' => 'application/json'], … … 56 80 ]); 57 81 82 self::closeChallenge(); 83 58 84 if (is_wp_error($response)) { 59 85 return ['success' => false, 'message' => $response->get_error_message()]; … … 93 119 94 120 if (!empty($licenseKey)) { 121 self::openChallenge(); 95 122 wp_remote_post(self::panelUrl() . '/api/plugin/disconnect', [ 96 123 'headers' => ['Content-Type' => 'application/json', 'Accept' => 'application/json'], … … 102 129 'sslverify' => ! preg_match('#(localhost|127\.0\.0\.1|\.local)#', self::panelUrl()), 103 130 ]); 131 self::closeChallenge(); 104 132 } 105 133 -
gen-wave/trunk/src/Core/AiRestApi.php
r3714852 r3763844 50 50 * runs its own permission_callback afterwards, so nothing is actually loosened. 51 51 * 52 * Two things are never cleared (1.2.2): 53 * - core's own cookie check (`rest_cookie_invalid_nonce`): with a wrong nonce 54 * WordPress keeps the cookie user logged in and only this error stops the 55 * request, so clearing it turned any cookie request into a CSRF; 56 * - anything that is not our route. The route is the one WordPress parsed 57 * (`rest_route`), not a substring of the raw URI: a query string such as 58 * `/wp-json/wp/v2/users?x=/wp-json/gen-wave/v1/` used to count as ours. 59 * 52 60 * @param mixed $result null (no auth attempted), true (allowed) or WP_Error/false (denied). 53 61 * @return mixed … … 60 68 } 61 69 70 // Core's cookie authentication said no: that answer stands. 71 if (is_wp_error($result) && in_array('rest_cookie_invalid_nonce', $result->get_error_codes(), true)) { 72 return $result; 73 } 74 75 return $this->is_own_route($this->current_rest_route()) ? true : $result; 76 } 77 78 /** The REST route WordPress is serving (e.g. "/gen-wave/v1/verify-domain"), or ''. */ 79 private function current_rest_route(): string 80 { 81 if (isset($GLOBALS['wp']) && is_object($GLOBALS['wp']) && !empty($GLOBALS['wp']->query_vars['rest_route'])) { 82 return (string) $GLOBALS['wp']->query_vars['rest_route']; 83 } 62 84 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only route match, no state change 63 $uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : ''; 64 if (!is_string($uri) || $uri === '') { 65 return $result; 66 } 67 68 $prefix = rest_get_url_prefix(); // usually "wp-json" 69 $ns = $this->namespace; // "gen-wave/v1" 70 $ours = 71 strpos($uri, '/' . $prefix . '/' . $ns . '/') !== false || // pretty permalinks 72 strpos($uri, 'rest_route=/' . $ns . '/') !== false || // plain permalinks 73 strpos($uri, 'rest_route=' . rawurlencode('/' . $ns . '/')) !== false; // encoded 74 75 return $ours ? true : $result; 85 if (isset($_GET['rest_route']) && is_string($_GET['rest_route'])) { 86 // phpcs:ignore WordPress.Security.NonceVerification.Recommended 87 return sanitize_text_field(wp_unslash($_GET['rest_route'])); 88 } 89 return ''; 90 } 91 92 /** Whether a REST route is in this plugin's namespace (the route itself, never its query). */ 93 public function is_own_route(string $route): bool 94 { 95 $route = '/' . ltrim($route, '/'); 96 return strpos($route, '/' . $this->namespace . '/') === 0; 76 97 } 77 98 … … 449 470 450 471 // Secure challenge-response — proves domain control + key possession. 472 // Answered only while this site is itself connecting or disconnecting 473 // (AgentAuth::openChallenge, 1.2.2): otherwise anyone holding the license 474 // key could have the panel "connect" this site for them and receive its 475 // signing key. 451 476 if (!empty($challenge)) { 477 if (!AgentAuth::challengeOpen()) { 478 return new WP_REST_Response([ 479 'success' => false, 480 'message' => __('This site is not connecting right now. Connect it from its Genwave settings.', 'gen-wave') 481 ], 403); 482 } 452 483 $licenseKey = Config::get('license_key'); 453 484 if (empty($licenseKey)) { … … 467 498 } 468 499 469 // Legacy token echo (unchanged). 470 if (empty($token)) { 471 return new WP_REST_Response([ 472 'success' => false, 473 'message' => __('Verification token is required', 'gen-wave') 474 ], 400); 475 } 476 477 // NOTE (F10): the exact plugin version is intentionally NOT included — this 478 // endpoint is public (permission_callback __return_true), and leaking the 479 // precise version lets an unauthenticated caller fingerprint the site. 500 // The legacy token echo (retired account.genwave.ai flow) is gone (1.2.2): it 501 // repeated any token back, so anyone could "verify" any site with it. 502 unset($token); 480 503 return new WP_REST_Response([ 481 'success' => true, 482 'message' => __('Genwave plugin is active', 'gen-wave'), 483 'token' => $token, 484 'domain' => get_site_url(), 485 'verified_at' => current_time('mysql') 486 ], 200); 504 'success' => false, 505 'message' => __('A verification challenge is required', 'gen-wave') 506 ], 400); 487 507 } 488 508 } -
gen-wave/trunk/src/Handlers/GenerationHandler.php
r3642637 r3763844 249 249 if (defined('WP_DEBUG') && WP_DEBUG) { 250 250 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug mode only 251 error_log("Genwave: License Key: " . $license_key);251 error_log("Genwave: Has License Key: " . ($license_key ? 'YES' : 'NO')); 252 252 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug mode only 253 253 error_log("Genwave: Has Token: " . ($encrypted_token ? 'YES' : 'NO')); -
gen-wave/trunk/src/Handlers/ProductHandler.php
r3642637 r3763844 250 250 } 251 251 252 // A nonce proves where the request came from, not who may read the post (1.2.2). 253 if (!current_user_can('edit_post', $post_id)) { 254 wp_send_json_error(__('You do not have permission to read this post.', 'gen-wave'), 403); 255 return; 256 } 257 252 258 // Get the post 253 259 $post = get_post($post_id); -
gen-wave/trunk/vendor/composer/installed.php
r3724835 r3763844 4 4 'pretty_version' => 'dev-master', 5 5 'version' => 'dev-master', 6 'reference' => ' 9f21dd8efa8e041d7d601e31c3813bd25a8ed4e6',6 'reference' => '8b75da2c8b8dc14d65d95387e3657e296bb2337f', 7 7 'type' => 'wordpress-plugin', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-master', 15 15 'version' => 'dev-master', 16 'reference' => ' 9f21dd8efa8e041d7d601e31c3813bd25a8ed4e6',16 'reference' => '8b75da2c8b8dc14d65d95387e3657e296bb2337f', 17 17 'type' => 'wordpress-plugin', 18 18 'install_path' => __DIR__ . '/../../',
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)