diff --git a/README.txt b/README.txt
index 7e04a2c..2a8a9ab 100644
--- a/README.txt
+++ b/README.txt
@@ -1,9 +1,9 @@
=== GLS Shipping for WooCommerce ===
Contributors: goran87
Tags: gls, shipping, woocommerce shipping
-Requires at least: 4.4
+Requires at least: 5.9
Tested up to: 6.9
-Stable tag: 1.4.0
+Stable tag: 1.4.1
License: GPLv2
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -81,6 +81,10 @@ To install and configure this plugin:
== Changelog ==
+= 1.4.1 =
+* Fix: Fixed Reflected Cross-Site Scripting (XSS) vulnerability in bulk action admin notices via the 'failed_orders' parameter. Added proper input sanitization and output escaping.
+* Fix: Added proper exception handling for GLS API errors in bulk label operations to prevent fatal errors. API errors are now displayed as admin notices instead of causing crashes.
+
= 1.4.0 =
* Secure PDF Label Storage: Labels are now stored in a protected directory with authentication-based download.
* Added Hungary Locker Saturation Filter for parcel locker map (filter-saturation attribute).
diff --git a/gls-shipping-for-woocommerce.php b/gls-shipping-for-woocommerce.php
index 12d509a..b8c0905 100644
--- a/gls-shipping-for-woocommerce.php
+++ b/gls-shipping-for-woocommerce.php
@@ -3,7 +3,7 @@
/**
* Plugin Name: GLS Shipping for WooCommerce
* Description: Offical GLS Shipping for WooCommerce plugin
- * Version: 1.4.0
+ * Version: 1.4.1
* Author: Inchoo
* License: GPLv2
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -24,7 +24,7 @@ final class GLS_Shipping_For_Woo
{
private static $instance;
- private $version = '1.4.0';
+ private $version = '1.4.1';
private function __construct()
{
@@ -173,16 +173,16 @@ public function handle_label_download()
}
// Verify nonce
- if (!isset($_GET['nonce']) || !wp_verify_nonce(sanitize_text_field($_GET['nonce']), 'gls_download_label')) {
+ if (!isset($_GET['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['nonce'])), 'gls_download_label')) {
wp_die(esc_html__('Invalid security token. Please refresh the page and try again.', 'gls-shipping-for-woocommerce'));
}
// Check user permissions
if (!current_user_can('edit_shop_orders')) {
- wp_die(__('You do not have permission to download shipping labels.', 'gls-shipping-for-woocommerce'));
+ wp_die(esc_html__('You do not have permission to download shipping labels.', 'gls-shipping-for-woocommerce'));
}
- $file_id = sanitize_file_name($_GET['gls_download_label']);
+ $file_id = sanitize_file_name(wp_unslash($_GET['gls_download_label']));
$file_path = GLS_LABELS_DIR . '/' . $file_id;
// Security check - ensure file is within labels directory
@@ -190,22 +190,33 @@ public function handle_label_download()
$real_labels_dir = realpath(GLS_LABELS_DIR);
if ($real_path === false || strpos($real_path, $real_labels_dir) !== 0) {
- wp_die(__('Invalid file path.', 'gls-shipping-for-woocommerce'));
+ wp_die(esc_html__('Invalid file path.', 'gls-shipping-for-woocommerce'));
}
if (!file_exists($file_path)) {
- wp_die(__('PDF label not found.', 'gls-shipping-for-woocommerce'));
+ wp_die(esc_html__('PDF label not found.', 'gls-shipping-for-woocommerce'));
+ }
+
+ // Serve the file using WP_Filesystem
+ global $wp_filesystem;
+ if (empty($wp_filesystem)) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ WP_Filesystem();
+ }
+
+ $file_contents = $wp_filesystem->get_contents($file_path);
+ if (false === $file_contents) {
+ wp_die(esc_html__('Could not read PDF file.', 'gls-shipping-for-woocommerce'));
}
- // Serve the file
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . basename($file_path) . '"');
header('Content-Transfer-Encoding: binary');
- header('Content-Length: ' . filesize($file_path));
+ header('Content-Length: ' . strlen($file_contents));
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
- readfile($file_path);
+ echo $file_contents; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Binary PDF data
exit;
}
@@ -259,6 +270,7 @@ public static function get_secure_label_url($order_id)
public function load_textdomain()
{
+ // phpcs:ignore PluginCheck.CodeAnalysis.DiscouragedFunctions.load_plugin_textdomainFound -- Manual loading needed for non-wp.org distribution
load_plugin_textdomain('gls-shipping-for-woocommerce', false, basename(dirname(__FILE__)) . '/languages/');
}
diff --git a/includes/admin/class-gls-shipping-bulk.php b/includes/admin/class-gls-shipping-bulk.php
index defb256..4b1b8c2 100644
--- a/includes/admin/class-gls-shipping-bulk.php
+++ b/includes/admin/class-gls-shipping-bulk.php
@@ -113,101 +113,116 @@ public function process_bulk_gls_label_generation($redirect, $doaction, $order_i
}
// Bulk print labels, dont generate for each but just print in single PDF
if ('print_gls_labels' === $doaction) {
- $prepare_data = new GLS_Shipping_API_Data($order_ids);
- $data = $prepare_data->generate_post_fields_multi();
-
- // Send order to GLS API
- $is_multi = true;
- $api = new GLS_Shipping_API_Service();
- $result = $api->send_order($data, $is_multi);
+ try {
+ $prepare_data = new GLS_Shipping_API_Data($order_ids);
+ $data = $prepare_data->generate_post_fields_multi();
+
+ // Send order to GLS API
+ $is_multi = true;
+ $api = new GLS_Shipping_API_Service();
+ $result = $api->send_order($data, $is_multi);
- $body = $result['body'];
- $failed_orders = $result['failed_orders'];
-
- // Check if all orders failed - don't attempt PDF creation if no successful labels
- if (count($failed_orders) >= count($order_ids)) {
- $redirect = add_query_arg(
- array(
- 'bulk_action' => 'print_gls_labels',
- 'gls_labels_printed' => 0,
- 'gls_labels_failed' => count($failed_orders),
- 'failed_orders' => implode(',', array_column($failed_orders, 'order_id')),
- ),
- $redirect
- );
- return $redirect;
- }
-
- $pdf_filename = $this->bulk_create_print_labels($body);
-
- if ($pdf_filename) {
- // Save tracking numbers to order meta
- if (!empty($body['PrintLabelsInfoList'])) {
- // Group tracking codes by order ID to handle multiple parcels per order
- $orders_data = array();
-
- foreach ($body['PrintLabelsInfoList'] as $labelInfo) {
- if (isset($labelInfo['ClientReference'])) {
- $order_id = str_replace('Order:', '', $labelInfo['ClientReference']);
-
- if (!isset($orders_data[$order_id])) {
- $orders_data[$order_id] = array(
- 'tracking_codes' => array(),
- 'parcel_ids' => array()
- );
- }
-
- if (isset($labelInfo['ParcelNumber'])) {
- $orders_data[$order_id]['tracking_codes'][] = $labelInfo['ParcelNumber'];
- }
- if (isset($labelInfo['ParcelId'])) {
- $orders_data[$order_id]['parcel_ids'][] = $labelInfo['ParcelId'];
+ $body = $result['body'];
+ $failed_orders = $result['failed_orders'];
+
+ // Check if all orders failed - don't attempt PDF creation if no successful labels
+ if (count($failed_orders) >= count($order_ids)) {
+ $redirect = add_query_arg(
+ array(
+ 'bulk_action' => 'print_gls_labels',
+ 'gls_labels_printed' => 0,
+ 'gls_labels_failed' => count($failed_orders),
+ 'failed_orders' => implode(',', array_column($failed_orders, 'order_id')),
+ ),
+ $redirect
+ );
+ return $redirect;
+ }
+
+ $pdf_filename = $this->bulk_create_print_labels($body);
+
+ if ($pdf_filename) {
+ // Save tracking numbers to order meta
+ if (!empty($body['PrintLabelsInfoList'])) {
+ // Group tracking codes by order ID to handle multiple parcels per order
+ $orders_data = array();
+
+ foreach ($body['PrintLabelsInfoList'] as $labelInfo) {
+ if (isset($labelInfo['ClientReference'])) {
+ $order_id = str_replace('Order:', '', $labelInfo['ClientReference']);
+
+ if (!isset($orders_data[$order_id])) {
+ $orders_data[$order_id] = array(
+ 'tracking_codes' => array(),
+ 'parcel_ids' => array()
+ );
+ }
+
+ if (isset($labelInfo['ParcelNumber'])) {
+ $orders_data[$order_id]['tracking_codes'][] = $labelInfo['ParcelNumber'];
+ }
+ if (isset($labelInfo['ParcelId'])) {
+ $orders_data[$order_id]['parcel_ids'][] = $labelInfo['ParcelId'];
+ }
}
}
- }
-
- // Now save all tracking codes for each order
- $successful_orders = array();
- foreach ($orders_data as $order_id => $data) {
- $order = wc_get_order($order_id);
- if ($order) {
- if (!empty($data['tracking_codes'])) {
- $order->update_meta_data('_gls_tracking_codes', $data['tracking_codes']);
+
+ // Now save all tracking codes for each order
+ $successful_orders = array();
+ foreach ($orders_data as $order_id => $data) {
+ $order = wc_get_order($order_id);
+ if ($order) {
+ if (!empty($data['tracking_codes'])) {
+ $order->update_meta_data('_gls_tracking_codes', $data['tracking_codes']);
+ }
+ if (!empty($data['parcel_ids'])) {
+ $order->update_meta_data('_gls_parcel_ids', $data['parcel_ids']);
+ }
+
+ // Save just the filename, URL with nonce is generated on display
+ $order->update_meta_data('_gls_print_label', $pdf_filename);
+ $order->save();
+
+ $successful_orders[] = $order_id;
}
- if (!empty($data['parcel_ids'])) {
- $order->update_meta_data('_gls_parcel_ids', $data['parcel_ids']);
- }
-
- // Save just the filename, URL with nonce is generated on display
- $order->update_meta_data('_gls_print_label', $pdf_filename);
- $order->save();
-
- $successful_orders[] = $order_id;
}
+
+ // Fire hook after successful bulk label generation
+ do_action('gls_bulk_labels_generated', $order_ids, $successful_orders, $failed_orders);
}
-
- // Fire hook after successful bulk label generation
- do_action('gls_bulk_labels_generated', $order_ids, $successful_orders, $failed_orders);
- }
- // Add query args to URL for displaying notices and providing PDF link
- $pdf_url = GLS_Shipping_For_Woo::get_label_download_url($pdf_filename);
- $redirect = add_query_arg(
- array(
- 'bulk_action' => 'print_gls_labels',
- 'gls_labels_printed' => count($order_ids) - count($failed_orders),
- 'gls_labels_failed' => count($failed_orders),
- 'gls_pdf_url' => urlencode($pdf_url),
- 'failed_orders' => implode(',', array_column($failed_orders, 'order_id')),
- ),
- $redirect
- );
- } else {
- // Handle error case
+ // Add query args to URL for displaying notices and providing PDF link
+ $pdf_url = GLS_Shipping_For_Woo::get_label_download_url($pdf_filename);
+ $redirect = add_query_arg(
+ array(
+ 'bulk_action' => 'print_gls_labels',
+ 'gls_labels_printed' => count($order_ids) - count($failed_orders),
+ 'gls_labels_failed' => count($failed_orders),
+ 'gls_pdf_url' => urlencode($pdf_url),
+ 'failed_orders' => implode(',', array_column($failed_orders, 'order_id')),
+ ),
+ $redirect
+ );
+ } else {
+ // Handle error case
+ $redirect = add_query_arg(
+ array(
+ 'bulk_action' => 'print_gls_labels',
+ 'gls_labels_printed_error' => 'true',
+ ),
+ $redirect
+ );
+ }
+ } catch (Exception $e) {
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional error logging for debugging
+ error_log('GLS Bulk Print Labels Error: ' . $e->getMessage());
+
+ // Handle the exception gracefully - redirect with error message
$redirect = add_query_arg(
array(
'bulk_action' => 'print_gls_labels',
'gls_labels_printed_error' => 'true',
+ 'gls_error_message' => urlencode($e->getMessage()),
),
$redirect
);
@@ -226,6 +241,7 @@ public function bulk_create_print_labels($body)
// Check if Labels exist and is an array
if (empty($body['Labels']) || !is_array($body['Labels'])) {
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional error logging for debugging
error_log('GLS Bulk Print: No labels found in API response. This may happen if all orders failed validation.');
return false;
}
@@ -248,17 +264,33 @@ public function bulk_create_print_labels($body)
}
// Display admin notice after bulk action
+ // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Display-only notice after redirect, nonce verified in original action
public function gls_bulk_action_admin_notice() {
if (isset($_REQUEST['bulk_action'])) {
- if ('generate_gls_labels' == $_REQUEST['bulk_action']) {
- $generated = intval($_REQUEST['gls_labels_generated']);
- $failed = intval($_REQUEST['gls_labels_failed']);
- $failed_orders = isset($_REQUEST['failed_orders']) ? explode(',', $_REQUEST['failed_orders']) : [];
+ // Sanitize the bulk action parameter
+ $bulk_action = sanitize_text_field(wp_unslash($_REQUEST['bulk_action']));
+
+ if ('generate_gls_labels' === $bulk_action) {
+ $generated = isset($_REQUEST['gls_labels_generated']) ? intval($_REQUEST['gls_labels_generated']) : 0;
+ $failed = isset($_REQUEST['gls_labels_failed']) ? intval($_REQUEST['gls_labels_failed']) : 0;
+
+ // Sanitize failed_orders - only allow integers (order IDs)
+ $failed_orders = array();
+ if (isset($_REQUEST['failed_orders']) && !empty($_REQUEST['failed_orders'])) {
+ $raw_failed_orders = sanitize_text_field(wp_unslash($_REQUEST['failed_orders']));
+ $failed_orders_array = explode(',', $raw_failed_orders);
+ foreach ($failed_orders_array as $order_id) {
+ $sanitized_id = absint(trim($order_id));
+ if ($sanitized_id > 0) {
+ $failed_orders[] = $sanitized_id;
+ }
+ }
+ }
// Prepare success message
$message = sprintf(
+ /* translators: %s: number of generated labels */
_n(
- /* translators: %s: number of generated labels */
'%s GLS label was successfully generated.',
'%s GLS labels were successfully generated.',
$generated,
@@ -266,12 +298,12 @@ public function gls_bulk_action_admin_notice() {
),
number_format_i18n($generated)
);
-
+
// Add failure message if any labels failed to generate
if ($failed > 0) {
$message .= ' ' . sprintf(
+ /* translators: %s: number of failed labels */
_n(
- /* translators: %s: number of failed labels */
'%s label failed to generate.',
'%s labels failed to generate.',
$failed,
@@ -279,26 +311,43 @@ public function gls_bulk_action_admin_notice() {
),
number_format_i18n($failed)
);
- $message .= ' ' . sprintf(
- /* translators: %s: comma-separated list of order IDs that failed */
- __('Failed order IDs: %s', 'gls-shipping-for-woocommerce'),
- implode(', ', $failed_orders)
- );
+ if (!empty($failed_orders)) {
+ $message .= ' ' . sprintf(
+ /* translators: %s: comma-separated list of order IDs that failed */
+ __('Failed order IDs: %s', 'gls-shipping-for-woocommerce'),
+ esc_html(implode(', ', $failed_orders))
+ );
+ }
}
- // Display the notice
- printf('
');
- } elseif ('print_gls_labels' == $_REQUEST['bulk_action']) {
+ // Display the notice with proper escaping
+ printf(
+ '',
+ wp_kses_post($message)
+ );
+ } elseif ('print_gls_labels' === $bulk_action) {
if (isset($_REQUEST['gls_labels_printed']) && isset($_REQUEST['gls_pdf_url'])) {
$printed = intval($_REQUEST['gls_labels_printed']);
- $failed = intval($_REQUEST['gls_labels_failed']);
- $pdf_url = urldecode($_REQUEST['gls_pdf_url']);
- $failed_orders = isset($_REQUEST['failed_orders']) ? explode(',', $_REQUEST['failed_orders']) : [];
+ $failed = isset($_REQUEST['gls_labels_failed']) ? intval($_REQUEST['gls_labels_failed']) : 0;
+ $pdf_url = esc_url_raw(urldecode(sanitize_text_field(wp_unslash($_REQUEST['gls_pdf_url']))));
+
+ // Sanitize failed_orders - only allow integers (order IDs)
+ $failed_orders = array();
+ if (isset($_REQUEST['failed_orders']) && !empty($_REQUEST['failed_orders'])) {
+ $raw_failed_orders = sanitize_text_field(wp_unslash($_REQUEST['failed_orders']));
+ $failed_orders_array = explode(',', $raw_failed_orders);
+ foreach ($failed_orders_array as $order_id) {
+ $sanitized_id = absint(trim($order_id));
+ if ($sanitized_id > 0) {
+ $failed_orders[] = $sanitized_id;
+ }
+ }
+ }
// Prepare success message
$message = sprintf(
+ /* translators: %s: number of orders processed */
_n(
- /* translators: %s: number of orders processed */
'GLS label for %s order has been generated. ',
'GLS labels for %s orders have been generated. ',
$printed,
@@ -310,8 +359,8 @@ public function gls_bulk_action_admin_notice() {
// Add failure message if any labels failed to generate
if ($failed > 0) {
$message .= sprintf(
+ /* translators: %s: number of failed labels */
_n(
- /* translators: %s: number of failed labels */
'%s label failed to generate. ',
'%s labels failed to generate. ',
$failed,
@@ -319,27 +368,49 @@ public function gls_bulk_action_admin_notice() {
),
number_format_i18n($failed)
);
- $message .= sprintf(
- __('Failed order IDs: %s', 'gls-shipping-for-woocommerce'),
- implode(', ', $failed_orders)
- );
+ if (!empty($failed_orders)) {
+ $message .= sprintf(
+ /* translators: %s: comma-separated list of order IDs that failed */
+ __('Failed order IDs: %s', 'gls-shipping-for-woocommerce'),
+ esc_html(implode(', ', $failed_orders))
+ );
+ }
}
-
+
$message .= sprintf(
/* translators: %s: URL to download the PDF file */
__('
Click here to download the PDF', 'gls-shipping-for-woocommerce'),
esc_url($pdf_url)
);
- // Display the notice
- printf('');
+ // Display the notice with proper escaping
+ printf(
+ '',
+ wp_kses_post($message)
+ );
} elseif (isset($_REQUEST['gls_labels_printed_error'])) {
$message = __('An error occurred while generating the GLS labels PDF.', 'gls-shipping-for-woocommerce');
- printf('');
+
+ // Display specific error message if available
+ if (isset($_REQUEST['gls_error_message']) && !empty($_REQUEST['gls_error_message'])) {
+ // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized via sanitize_text_field after urldecode
+ $error_detail = sanitize_text_field(urldecode(wp_unslash($_REQUEST['gls_error_message'])));
+ $message .= ' ' . sprintf(
+ /* translators: %s: error message from GLS API */
+ __('Error: %s', 'gls-shipping-for-woocommerce'),
+ $error_detail
+ );
+ }
+
+ printf(
+ '',
+ esc_html($message)
+ );
}
}
}
}
+ // phpcs:enable WordPress.Security.NonceVerification.Recommended
// Enqueue bulk styles
public function admin_enqueue_styles()
@@ -440,9 +511,9 @@ private function display_parcel_ids($order_id)
}
}
- // Display the tracking numbers
+ // Display the tracking numbers (each element is already escaped with esc_html())
if (!empty($tracking_numbers)) {
- echo implode(' ', $tracking_numbers);
+ echo wp_kses_post( implode(' ', $tracking_numbers) );
} else {
echo '-';
}
diff --git a/includes/admin/class-gls-shipping-label-migration.php b/includes/admin/class-gls-shipping-label-migration.php
index 6a33959..9e7d44e 100644
--- a/includes/admin/class-gls-shipping-label-migration.php
+++ b/includes/admin/class-gls-shipping-label-migration.php
@@ -116,21 +116,24 @@ private function has_orders_needing_migration()
Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled()) {
// HPOS enabled
$table = $wpdb->prefix . 'wc_orders_meta';
+ // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name uses $wpdb->prefix + constant
$exists = $wpdb->get_var($wpdb->prepare(
- "SELECT 1 FROM {$table}
- WHERE meta_key = '_gls_print_label'
- AND meta_value LIKE %s
+ "SELECT 1 FROM {$table}
+ WHERE meta_key = '_gls_print_label'
+ AND meta_value LIKE %s
AND meta_value NOT LIKE %s
LIMIT 1",
'%/wp-content/uploads/%',
'%gls_download_label%'
));
+ // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
} else {
// Legacy post meta
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Using $wpdb->postmeta property
$exists = $wpdb->get_var($wpdb->prepare(
- "SELECT 1 FROM {$wpdb->postmeta}
- WHERE meta_key = '_gls_print_label'
- AND meta_value LIKE %s
+ "SELECT 1 FROM {$wpdb->postmeta}
+ WHERE meta_key = '_gls_print_label'
+ AND meta_value LIKE %s
AND meta_value NOT LIKE %s
LIMIT 1",
'%/wp-content/uploads/%',
@@ -156,22 +159,25 @@ private function get_orders_needing_migration($limit)
Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled()) {
// HPOS enabled
$table = $wpdb->prefix . 'wc_orders_meta';
+ // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name uses $wpdb->prefix + constant
$order_ids = $wpdb->get_col($wpdb->prepare(
- "SELECT order_id FROM {$table}
- WHERE meta_key = '_gls_print_label'
- AND meta_value LIKE %s
+ "SELECT order_id FROM {$table}
+ WHERE meta_key = '_gls_print_label'
+ AND meta_value LIKE %s
AND meta_value NOT LIKE %s
LIMIT %d",
'%/wp-content/uploads/%',
'%gls_download_label%',
$limit
));
+ // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
} else {
// Legacy post meta
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Using $wpdb->postmeta property
$order_ids = $wpdb->get_col($wpdb->prepare(
- "SELECT post_id FROM {$wpdb->postmeta}
- WHERE meta_key = '_gls_print_label'
- AND meta_value LIKE %s
+ "SELECT post_id FROM {$wpdb->postmeta}
+ WHERE meta_key = '_gls_print_label'
+ AND meta_value LIKE %s
AND meta_value NOT LIKE %s
LIMIT %d",
'%/wp-content/uploads/%',
@@ -278,7 +284,7 @@ private function migrate_single_label($order_id)
$order->save();
// Delete old file
- @unlink($old_path);
+ wp_delete_file($old_path);
return true;
}
@@ -330,13 +336,13 @@ private function cleanup_orphaned_labels()
continue;
}
foreach ($files as $file) {
- if (@unlink($file)) {
- $deleted_count++;
- }
+ wp_delete_file($file);
+ $deleted_count++;
}
}
if ($deleted_count > 0) {
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional logging for migration process
error_log("GLS Migration: Cleaned up {$deleted_count} orphaned label files from old uploads folders.");
}
}
@@ -373,7 +379,7 @@ public function handle_old_label_fallback()
}
// Verify nonce
- if (!wp_verify_nonce(sanitize_text_field($_GET['nonce']), 'gls_old_label_access')) {
+ if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['nonce'])), 'gls_old_label_access')) {
wp_die(esc_html__('Invalid security token.', 'gls-shipping-for-woocommerce'));
}
@@ -403,15 +409,26 @@ public function handle_old_label_fallback()
wp_die(esc_html__('PDF label file not found.', 'gls-shipping-for-woocommerce'));
}
- // Serve the file
+ // Serve the file using WP_Filesystem
+ global $wp_filesystem;
+ if (empty($wp_filesystem)) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ WP_Filesystem();
+ }
+
+ $file_contents = $wp_filesystem->get_contents($file_path);
+ if (false === $file_contents) {
+ wp_die(esc_html__('Could not read PDF file.', 'gls-shipping-for-woocommerce'));
+ }
+
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . basename($file_path) . '"');
header('Content-Transfer-Encoding: binary');
- header('Content-Length: ' . filesize($file_path));
+ header('Content-Length: ' . strlen($file_contents));
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
- readfile($file_path);
+ echo $file_contents; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Binary PDF data
exit;
}
}
diff --git a/includes/admin/class-gls-shipping-order.php b/includes/admin/class-gls-shipping-order.php
index f971180..53db6a7 100644
--- a/includes/admin/class-gls-shipping-order.php
+++ b/includes/admin/class-gls-shipping-order.php
@@ -182,7 +182,10 @@ public function gls_shipping_info_meta_box_content($order_or_post_id)
- render_service_options($order); ?>
+ render_service_options($order);
+ ?>