--- a/seo-links-interlinking/scdata.php
+++ b/seo-links-interlinking/scdata.php
@@ -5,7 +5,7 @@
* Description: Automate internal link building in WordPress with AI-powered suggestions from Google Search Console. Build better SEO with intelligent interlinking.
* Author: WP SEO Plugins
* Author URI: https://wpseoplugins.org/
- * Version: 1.7.9.9.1
+ * Version: 1.7.9.9.2
* Text Domain: seo-links-interlinking
* Domain Path: /languages
*/
@@ -37,7 +37,7 @@
// Usa funzioni WordPress per URL sicuri invece di $_SERVER
define( 'SEOLI_SITE_URL', site_url() );
define( 'SEOLI_SERVER_REQUEST_URI', esc_url_raw( $_SERVER['REQUEST_URI'] ) );
-define( 'SEOLI_VERSION', '1.7.9.9.1' );
+define( 'SEOLI_VERSION', '1.7.9.9.2' );
/**
* Load plugin textdomain for translations
@@ -381,7 +381,23 @@
console.log( response );
if( response.status == -1 || response.status == -3 || response.status == -4 ){
- window.location.href = window.location.href + '&response_status=' + response.status + '&google_error=' + response.message;
+ // SECURITY FIX CVE-2025-14063: Use encodeURIComponent to prevent XSS
+ // Properly encode URL parameters to prevent injection attacks
+ const errorParam = encodeURIComponent( response.message || '' );
+ const statusParam = encodeURIComponent( response.status || '' );
+
+ // Use URL API for safe URL construction
+ try {
+ const currentUrl = new URL( window.location.href );
+ currentUrl.searchParams.set( 'response_status', statusParam );
+ currentUrl.searchParams.set( 'google_error', errorParam );
+ window.location.href = currentUrl.toString();
+ } catch( e ) {
+ // Fallback for older browsers: manually construct URL with encoded params
+ const baseUrl = window.location.href.split('&response_status')[0].split('&google_error')[0].split('?')[0];
+ const separator = window.location.href.indexOf('?') !== -1 ? '&' : '?';
+ window.location.href = baseUrl + separator + 'response_status=' + statusParam + '&google_error=' + errorParam;
+ }
return;
}
@@ -731,14 +747,20 @@
</div>
<?php endif; ?>
<?php endif; ?>
- <?php if(isset($_GET['google_error'])) : ?>
+ <?php
+ // SECURITY FIX CVE-2025-14063: Use centralized sanitization function
+ $google_error_message = seoli_get_google_error_message();
+ if( $google_error_message !== false ) :
+ ?>
<script>
if( seoli_isGutenbergActive() ) {
+ // SECURITY FIX CVE-2025-14063: Removed __unstableHTML flag and use wp_json_encode for safe output
+ // wp_json_encode properly escapes the string for JavaScript context
wp.data.dispatch('core/notices').createErrorNotice(
- '<?php echo esc_js( esc_html( $_GET['google_error'] ) ); ?>',
+ <?php echo wp_json_encode( $google_error_message ); ?>,
{
- isDismissible: true,
- __unstableHTML: true
+ isDismissible: true
+ // SECURITY: Removed __unstableHTML: true to prevent XSS
}
);
} else {
@@ -751,7 +773,7 @@
</script>
<div class="notice notice-error is-dismissible">
<h2><?php echo esc_html( __("SEO Links Interlinking", 'seo-links-interlinking') ); ?></h2>
- <p style="font-size: 18px;"><?php echo esc_html( stripslashes( $_GET['google_error'] ) ); ?></p>
+ <p style="font-size: 18px;"><?php echo esc_html( $google_error_message ); ?></p>
<div id="error_modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
@@ -759,7 +781,7 @@
<h5 class="modal-title"><?php echo esc_html( __("SEO Links Interlinking", 'seo-links-interlinking') ); ?></h5>
</div>
<div class="modal-body">
- <p style="font-size: 18px;"><?php echo esc_html( stripslashes( $_GET['google_error'] ) ); ?></p>
+ <p style="font-size: 18px;"><?php echo esc_html( $google_error_message ); ?></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" onclick="jQuery('#error_modal').modal('hide');"><?php echo esc_html( __("Close", 'seo-links-interlinking') ); ?></button>
@@ -1140,6 +1162,21 @@
if( isset( $_GET['seoli_cron_trigger'] ) ) {
$provided_token = sanitize_text_field( $_GET['seoli_cron_trigger'] );
+ // Rate limiting: max 1 request per minute per IP
+ $rate_limit_key = 'seoli_cron_rate_limit_' . md5( $_SERVER['REMOTE_ADDR'] ?? 'unknown' );
+ $last_request = get_transient( $rate_limit_key );
+
+ if( $last_request !== false && ( time() - $last_request ) < 60 ) {
+ // Too many requests
+ header( 'Content-Type: application/json' );
+ http_response_code( 429 );
+ echo json_encode( array(
+ 'status' => 'error',
+ 'message' => 'Rate limit exceeded. Please wait before trying again.'
+ ) );
+ exit;
+ }
+
// If no token is set, generate one and save it
if( empty( $secret_token ) ) {
$secret_token = wp_generate_password( 32, false );
@@ -1148,6 +1185,9 @@
// Verify token
if( $provided_token === $secret_token ) {
+ // Update rate limit
+ set_transient( $rate_limit_key, time(), 60 );
+
// Trigger bulk queue processing
seoli_process_next_post_in_queue();
@@ -1224,7 +1264,8 @@
update_option( 'seoli_bulk_delay', $seoli_bulk_delay );
update_option( 'seoli_bulk_max_links', $seoli_bulk_max_links );
- wp_redirect( admin_url('admin.php?page=seo-links') );
+ wp_safe_redirect( admin_url('admin.php?page=seo-links') );
+ exit;
}
include SEOLI_PATH_ABS . 'view/seo_links_settings.php';
}
@@ -1375,10 +1416,19 @@
);
$data = wp_remote_get( $remote_get, $args );
+ if( is_wp_error( $data ) ) {
+ wp_die( esc_html( __("Error fetching data from server", 'seo-links-interlinking') ) );
+ }
+
$rowData = json_decode( $data['body'] );
+
+ // Verifica errori JSON
+ if( json_last_error() !== JSON_ERROR_NONE || ! is_object( $rowData ) ) {
+ wp_die( esc_html( __("Invalid JSON response from server", 'seo-links-interlinking') ) );
+ }
- if( $rowData->status == -1 || $rowData->status == -2 || $rowData->status == -3 || $rowData->status == -4 ){
- wp_die(json_encode($rowData));
+ if( isset( $rowData->status ) && ( $rowData->status == -1 || $rowData->status == -2 || $rowData->status == -3 || $rowData->status == -4 ) ){
+ wp_die( esc_html( isset( $rowData->message ) ? $rowData->message : __("API error", 'seo-links-interlinking') ) );
}
foreach( $rowData as $row ) {
@@ -2011,13 +2061,20 @@
add_action('admin_init', 'seoli_redirect');
function seoli_redirect() {
if (get_option('seoli_do_activation_redirect', false)) {
+ // Verifica capability prima di redirect
+ if( ! current_user_can( 'edit_posts' ) ) {
+ delete_option('seoli_do_activation_redirect');
+ return;
+ }
+
delete_option('seoli_do_activation_redirect');
$url = esc_url( add_query_arg(
'page',
'wp-seo-plugins-login',
get_admin_url() . 'admin.php'
) );
- wp_redirect( $url );
+ wp_safe_redirect( $url );
+ exit;
}
}
--- a/seo-links-interlinking/utils.php
+++ b/seo-links-interlinking/utils.php
@@ -67,4 +67,36 @@
*/
function seoli_get_languages() {
return array('it','es','en','de','nl','da','no','sv','fi','pl','pt','pt-br','tr','ru','zh','zh-tw','ja','hi','ko','en-uk');
+}
+
+/**
+ * Sanitize and validate google_error parameter
+ * Prevents XSS attacks via google_error parameter
+ *
+ * @param string $error_message Raw error message from $_GET['google_error']
+ * @return string|false Sanitized error message or false if invalid
+ */
+function seoli_get_google_error_message() {
+ if( ! isset( $_GET['google_error'] ) ) {
+ return false;
+ }
+
+ // Get raw input
+ $message = $_GET['google_error'];
+
+ // Sanitize text field (removes HTML tags and dangerous characters)
+ $message = sanitize_text_field( $message );
+
+ // Remove any remaining HTML/JS tags (double protection)
+ $message = wp_strip_all_tags( $message );
+
+ // Limit length to prevent DoS attacks (500 chars should be enough for error messages)
+ $message = substr( $message, 0, 500 );
+
+ // Return false if message is empty after sanitization
+ if( empty( trim( $message ) ) ) {
+ return false;
+ }
+
+ return $message;
}
No newline at end of file
--- a/seo-links-interlinking/view/seo_links_settings.php
+++ b/seo-links-interlinking/view/seo_links_settings.php
@@ -18,10 +18,21 @@
</script>
<?php endif; ?>
<?php endif; ?>
-<?php if(isset($_GET['google_error'])) : ?>
+<?php
+// SECURITY FIX CVE-2025-14063: Use centralized sanitization function
+$google_error_message = function_exists( 'seoli_get_google_error_message' ) ? seoli_get_google_error_message() : false;
+if( $google_error_message === false && isset( $_GET['google_error'] ) ) {
+ // Fallback sanitization if function not available
+ $google_error_message = sanitize_text_field( $_GET['google_error'] );
+ $google_error_message = wp_strip_all_tags( $google_error_message );
+ $google_error_message = substr( $google_error_message, 0, 500 );
+ $google_error_message = ! empty( trim( $google_error_message ) ) ? $google_error_message : false;
+}
+if( $google_error_message !== false ) :
+?>
<div class="notice notice-error is-dismissible">
<h2><?php echo esc_html( __("SEO Links Interlinking", 'seo-links-interlinking') ); ?></h2>
- <p style="font-size: 18px;"><?php echo esc_html( stripslashes( $_GET['google_error'] ) ); ?></p>
+ <p style="font-size: 18px;"><?php echo esc_html( $google_error_message ); ?></p>
</div>
<script>
// Cleaning url from data
@@ -568,7 +579,7 @@
?>
<div style="margin-bottom: 10px; padding: 8px; background: #fff; border-left: 3px solid #2271b1;">
<strong style="color: #666;">[<?php echo esc_html( $timestamp ); ?>]</strong>
- <strong><?php echo $message; ?></strong>
+ <strong><?php echo esc_html( $message ); ?></strong>
<?php if( $data ) : ?>
<details style="margin-top: 5px;">
<summary style="cursor: pointer; color: #0073aa;"><?php echo esc_html( __("View Data", 'seo-links-interlinking') ); ?></summary>
--- a/seo-links-interlinking/view/settings.php
+++ b/seo-links-interlinking/view/settings.php
@@ -34,10 +34,21 @@
</script>
<?php endif; ?>
<?php endif; ?>
- <?php if(isset($_GET['google_error'])) : ?>
+ <?php
+ // SECURITY FIX CVE-2025-14063: Use centralized sanitization function
+ $google_error_message = function_exists( 'seoli_get_google_error_message' ) ? seoli_get_google_error_message() : false;
+ if( $google_error_message === false && isset( $_GET['google_error'] ) ) {
+ // Fallback sanitization if function not available
+ $google_error_message = sanitize_text_field( $_GET['google_error'] );
+ $google_error_message = wp_strip_all_tags( $google_error_message );
+ $google_error_message = substr( $google_error_message, 0, 500 );
+ $google_error_message = ! empty( trim( $google_error_message ) ) ? $google_error_message : false;
+ }
+ if( $google_error_message !== false ) :
+ ?>
<div class="notice notice-error is-dismissible">
<h2><?php echo esc_html( __("SEO Links Interlinking", 'seo-links-interlinking') ); ?></h2>
- <p style="font-size: 18px;"><?php echo esc_html( stripslashes( $_GET['google_error'] ) ); ?></p>
+ <p style="font-size: 18px;"><?php echo esc_html( $google_error_message ); ?></p>
</div>
<script>
// Cleaning url from data
@@ -141,7 +152,7 @@
</tr>
<tr>
<th scope="row"></th>
- <td><a class="button button-secondary" href="https://www.wpseoplugins.org/wp-login.php?action=lostpassword" target="_blank"><?php echo esc_html( __("Forgot password?", 'seo-links-interlinking') ); ?></a></td>
+ <td><a class="button button-secondary" href="<?php echo esc_url( 'https://www.wpseoplugins.org/wp-login.php?action=lostpassword' ); ?>" target="_blank"><?php echo esc_html( __("Forgot password?", 'seo-links-interlinking') ); ?></a></td>
</tr>
<tr>
<td></td>
--- a/seo-links-interlinking/wp_seo_plugins.php
+++ b/seo-links-interlinking/wp_seo_plugins.php
@@ -50,11 +50,20 @@
'cookies' => array(),
);
$response = wp_remote_post( WP_SEO_PLUGINS_BACKEND_URL . 'registration', $args );
- $data = json_decode(wp_remote_retrieve_body( $response ));
+ $response_body = wp_remote_retrieve_body( $response );
+ $data = json_decode( $response_body );
+
+ // Verifica errori JSON
+ if( json_last_error() !== JSON_ERROR_NONE || ! is_object( $data ) ) {
+ $_SESSION['wp_seo_plugins_status'] = -1;
+ $_SESSION['wp_seo_plugins_message'] = sanitize_text_field( __("Invalid response from server", 'seo-links-interlinking') );
+ wp_safe_redirect( admin_url( 'admin.php?page=wp-seo-plugins-login' ) );
+ exit;
+ }
- $_SESSION['wp_seo_plugins_status'] = $data->status;
- $_SESSION['wp_seo_plugins_message'] = $data->message;
- $_SESSION['wp_seo_plugins_api_key'] = $data->api_key ?? '';
+ $_SESSION['wp_seo_plugins_status'] = isset( $data->status ) ? absint( $data->status ) : -1;
+ $_SESSION['wp_seo_plugins_message'] = isset( $data->message ) ? sanitize_text_field( $data->message ) : '';
+ $_SESSION['wp_seo_plugins_api_key'] = isset( $data->api_key ) ? sanitize_text_field( $data->api_key ) : '';
if( $_SESSION['wp_seo_plugins_api_key'] != '' ) {
update_option('sc_api_key', sanitize_text_field( $_SESSION['wp_seo_plugins_api_key'] ));
@@ -63,7 +72,7 @@
update_option('wp_seo_plugins_user_email', $user->data->user_email );
}
- wp_redirect( admin_url( 'admin.php?page=wp-seo-plugins-login' ) );
+ wp_safe_redirect( admin_url( 'admin.php?page=wp-seo-plugins-login' ) );
exit;
}
}
@@ -96,12 +105,21 @@
'cookies' => array(),
);
$response = wp_remote_post( WP_SEO_PLUGINS_BACKEND_URL . 'login', $args );
- $data = json_decode(wp_remote_retrieve_body( $response ));
+ $response_body = wp_remote_retrieve_body( $response );
+ $data = json_decode( $response_body );
+
+ // Verifica errori JSON
+ if( json_last_error() !== JSON_ERROR_NONE || ! is_object( $data ) ) {
+ $_SESSION['wp_seo_plugins_status'] = -1;
+ $_SESSION['wp_seo_plugins_message'] = sanitize_text_field( __("Invalid response from server", 'seo-links-interlinking') );
+ wp_safe_redirect( admin_url( 'admin.php?page=wp-seo-plugins-login' ) );
+ exit;
+ }
- $_SESSION['wp_seo_plugins_status'] = $data->status;
- $_SESSION['wp_seo_plugins_message'] = $data->message;
+ $_SESSION['wp_seo_plugins_status'] = isset( $data->status ) ? absint( $data->status ) : -1;
+ $_SESSION['wp_seo_plugins_message'] = isset( $data->message ) ? sanitize_text_field( $data->message ) : '';
- if($data->status == 0) {
+ if( isset( $data->status ) && $data->status == 0 ) {
// Generating a new api key
$server_uri = site_url();
@@ -118,11 +136,20 @@
'cookies' => array(),
);
$response = wp_remote_post( WP_SEO_PLUGINS_BACKEND_URL . 'apikey/generate', $args );
- $data = json_decode(wp_remote_retrieve_body( $response ));
+ $response_body = wp_remote_retrieve_body( $response );
+ $data = json_decode( $response_body );
+
+ // Verifica errori JSON
+ if( json_last_error() !== JSON_ERROR_NONE || ! is_object( $data ) ) {
+ $_SESSION['wp_seo_plugins_status'] = -1;
+ $_SESSION['wp_seo_plugins_message'] = sanitize_text_field( __("Invalid response from server", 'seo-links-interlinking') );
+ wp_safe_redirect( admin_url( 'admin.php?page=wp-seo-plugins-login' ) );
+ exit;
+ }
- $_SESSION['wp_seo_plugins_status'] = $data->status;
- $_SESSION['wp_seo_plugins_message'] = $data->message;
- $_SESSION['wp_seo_plugins_api_key'] = $data->api_key ?? '';
+ $_SESSION['wp_seo_plugins_status'] = isset( $data->status ) ? absint( $data->status ) : -1;
+ $_SESSION['wp_seo_plugins_message'] = isset( $data->message ) ? sanitize_text_field( $data->message ) : '';
+ $_SESSION['wp_seo_plugins_api_key'] = isset( $data->api_key ) ? sanitize_text_field( $data->api_key ) : '';
if( $_SESSION['wp_seo_plugins_api_key'] != '' ) {
update_option('sc_api_key', sanitize_text_field( $_SESSION['wp_seo_plugins_api_key']) );
@@ -132,7 +159,7 @@
}
}
- wp_redirect( admin_url( 'admin.php?page=wp-seo-plugins-login' ) );
+ wp_safe_redirect( admin_url( 'admin.php?page=wp-seo-plugins-login' ) );
exit;
}
}
@@ -160,6 +187,11 @@
$data = wp_remote_get( $remote_get, $args );
if( is_array( $data ) && !is_wp_error( $data ) ) {
$rowData = json_decode( $data['body'] );
+
+ // Verifica errori JSON
+ if( json_last_error() !== JSON_ERROR_NONE || ! is_object( $rowData ) || ! isset( $rowData->response ) ) {
+ return 0;
+ }
$response = $rowData->response;
$credits = new stdClass();
@@ -200,7 +232,7 @@
delete_option('sc_api_key');
delete_option('wp_seo_plugins_user_display_name');
delete_option('wp_seo_plugins_user_email');
- wp_redirect(admin_url('admin.php?page=wp-seo-plugins-login'));
+ wp_safe_redirect( admin_url('admin.php?page=wp-seo-plugins-login') );
exit;
}
}
No newline at end of file