-
Notifications
You must be signed in to change notification settings - Fork 83
Release: Hardening, bug fixes and improvements #450
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Remove integer cast from mobile number that truncated phone numbers
- Fix malformed format specifier in wpdb->delete (' % d' -> '%d')
- Use prepareMobileNumberQuery with IN clause for flexible phone matching
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
fix(privacy): resolve subscriber deletion not working
…image fix: resolve license status image size issue in header
Replaces 'wp_sms_enable_upgrade_to_bundle' filter with 'wp_sms_enable_upgrade_notice' in settings and send-sms templates. Adds new filter and logic in LicenseManagementManager to show upgrade notices only when user lacks both All-in-One and Pro licenses, while keeping the old filter for header notices when user lacks All-in-One license.
…ce-conditions Fix: Separate license conditions for header and notice display
…ailability-notification refactor: replace get_title() with get_name()
Bumps [qs](https://github.com/ljharb/qs) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together. Updates `qs` from 6.13.0 to 6.14.1 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](ljharb/qs@v6.13.0...v6.14.1) Updates `express` from 4.21.0 to 4.22.1 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/v4.22.1/History.md) - [Commits](expressjs/express@4.21.0...v4.22.1) --- updated-dependencies: - dependency-name: qs dependency-version: 6.14.1 dependency-type: indirect - dependency-name: express dependency-version: 4.22.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]>
The request() method now returns decoded JSON directly (stdClass/array) instead of raw WordPress HTTP response. Updated GetCredit() to work with this new return format. Fixes: PHP Fatal error: Cannot use object of type stdClass as array
This workflow triggers a sync with the gateway registry on push events to specified branches, on release publication, and via manual dispatch.
…fcbbcd8 build(deps): bump qs and express
The phone field placeholder (e.g., %your-phone%) was being passed directly to the SMS send function without being replaced with the actual form value. Now properly extracts the phone number from the notification variables.
Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](lodash/lodash@4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash-es dependency-version: 4.17.23 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]>
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](lodash/lodash@4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash dependency-version: 4.17.23 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]>
WalkthroughThis PR encompasses multiple improvements: adds a GitHub Actions workflow for gateway registry notifications, fixes privacy data deletion by refining mobile number queries and SQL placeholders, enhances security in admin outbox with SQL sanitization and output escaping, updates license management with a new upgrade notice filter, normalizes CSS formatting, and adds WooCommerce product name token support. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/Controller/PrivacyDataAjax.php (1)
65-68: Potential inconsistency: WordPress user query may not match phone variations.The
get_users()call passes an array fromprepareMobileNumberQuery()tometa_value, but withoutmeta_compare => 'IN', WordPress defaults to=comparison which won't correctly match any of the phone variations in the array.🔧 Proposed fix to use IN comparison for user meta query
$get_user = get_users([ 'meta_key' => Helper::getUserMobileFieldName(), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key - 'meta_value' => Helper::prepareMobileNumberQuery($mobile) // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value + 'meta_value' => Helper::prepareMobileNumberQuery($mobile), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value + 'meta_compare' => 'IN' // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value ]);includes/admin/settings/class-wpsms-settings.php (1)
1761-1782: Preserve backward compatibility for existing filters.Line 1761 switches to
wp_sms_enable_upgrade_notice, which can unintentionally re-enable notices for sites that previously disabled them viawp_sms_enable_upgrade_to_bundle. Consider chaining the legacy filter as a fallback to avoid regressions.🔧 Suggested backward‑compat change
- if (apply_filters('wp_sms_enable_upgrade_notice', true)) : + $showUpgradeNotice = apply_filters('wp_sms_enable_upgrade_notice', true); + $showUpgradeNotice = apply_filters('wp_sms_enable_upgrade_to_bundle', $showUpgradeNotice); + if ($showUpgradeNotice) :
🤖 Fix all issues with AI agents
In `@includes/admin/outbox/class-wpsms-outbox.php`:
- Around line 348-356: Normalize and validate incoming sort params before use:
run wp_unslash() and sanitize_key() on $_REQUEST['orderby'] and
sanitize_text_field/strtoupper on $_REQUEST['order'], then check the sanitized
orderby against the existing $allowed_columns array and only use that sanitized
variable in the SQL ORDER BY string (replace direct $_REQUEST['orderby'] usage)
and in the usort_reorder comparator; also reuse the same $allowed_columns
allowlist inside usort_reorder (or pass the validated key into it) so sorting
never operates on an invalid key and you have a consistent, notice-free ordering
behavior.
In `@includes/gateways/class-wpsms-gateway-smsto.php`:
- Around line 178-195: The balance handling assumes $responseObject is an
object, but request() may return an array; update the logic in the method in
class WPSMS_Gateway_SMSTO (the block that currently sets $responseObject =
$response and then checks $responseObject->balance / $responseObject->message)
to handle both arrays and objects: detect is_array($responseObject) and check
['balance'] and ['message'] keys (or normalize $responseObject to an object via
(object)$response), convert the balance to a float and round it, and return
WP_Error('account-credit', ...) when message exists for either type so array
responses no longer cause a false "Invalid response" error.
♻️ Duplicate comments (1)
includes/templates/admin/send-sms.php (1)
287-296: Same filter compatibility concern as settings page.Please see the note in
includes/admin/settings/class-wpsms-settings.php(Line 1761) about chaining the legacywp_sms_enable_upgrade_to_bundlefilter.
🧹 Nitpick comments (2)
.github/workflows/notify-gateway-registry.yml (2)
5-16: Set explicit minimalpermissionsfor the workflow.
This job uses a PAT and doesn’t need the default GITHUB_TOKEN scopes. Explicitly setting permissions reduces risk.♻️ Suggested change
on: push: branches: [master, development] paths: - 'includes/gateways/**' release: types: [published] workflow_dispatch: + +permissions: {}
19-25: Pin third-party action to a commit SHA.
Using a floating tag (@v3) increases supply-chain risk. Pin to a specific commit SHA from the upstream release.♻️ Example update
- uses: peter-evans/repository-dispatch@v3 + uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0
| // Allowed columns for ordering to prevent SQL injection | ||
| $allowed_columns = array('ID', 'sender', 'date', 'message', 'recipient', 'media', 'status'); | ||
| $allowed_orders = array('asc', 'desc'); | ||
|
|
||
| if (isset($_REQUEST['orderby']) && in_array($_REQUEST['orderby'], $allowed_columns, true)) { | ||
| $order_direction = isset($_REQUEST['order']) && in_array(strtolower($_REQUEST['order']), $allowed_orders, true) | ||
| ? strtoupper($_REQUEST['order']) | ||
| : 'ASC'; | ||
| $orderby .= "ORDER BY {$this->tb_prefix}sms_send.{$_REQUEST['orderby']} {$order_direction}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normalize request params and keep sorting consistent with usort_reorder.
Good allowlisting, but raw $_REQUEST values are still used and usort_reorder can still sort on an invalid key, causing notices or inconsistent ordering when orderby is invalid. Consider normalizing with wp_unslash() + sanitize_key() and reusing the same allowlist in usort_reorder.
🧩 Proposed tweak
- if (isset($_REQUEST['orderby']) && in_array($_REQUEST['orderby'], $allowed_columns, true)) {
- $order_direction = isset($_REQUEST['order']) && in_array(strtolower($_REQUEST['order']), $allowed_orders, true)
- ? strtoupper($_REQUEST['order'])
+ $orderby_key = isset($_REQUEST['orderby']) ? sanitize_key(wp_unslash($_REQUEST['orderby'])) : '';
+ $order_key = isset($_REQUEST['order']) ? strtolower(sanitize_key(wp_unslash($_REQUEST['order']))) : '';
+
+ if ($orderby_key && in_array($orderby_key, $allowed_columns, true)) {
+ $order_direction = in_array($order_key, $allowed_orders, true)
+ ? strtoupper($order_key)
: 'ASC';
- $orderby .= "ORDER BY {$this->tb_prefix}sms_send.{$_REQUEST['orderby']} {$order_direction}";
+ $orderby .= "ORDER BY {$this->tb_prefix}sms_send.{$orderby_key} {$order_direction}";
} else {
$orderby .= "ORDER BY date DESC";
}- $orderby = (!empty($_REQUEST['orderby'])) ? sanitize_text_field($_REQUEST['orderby']) : 'date';
- $order = (!empty($_REQUEST['order'])) ? sanitize_text_field($_REQUEST['order']) : 'desc';
+ $allowed_columns = array('ID', 'sender', 'date', 'message', 'recipient', 'media', 'status');
+ $allowed_orders = array('asc', 'desc');
+ $orderby = isset($_REQUEST['orderby']) ? sanitize_key(wp_unslash($_REQUEST['orderby'])) : 'date';
+ if (!in_array($orderby, $allowed_columns, true)) {
+ $orderby = 'date';
+ }
+ $order = isset($_REQUEST['order']) ? strtolower(sanitize_key(wp_unslash($_REQUEST['order']))) : 'desc';
+ if (!in_array($order, $allowed_orders, true)) {
+ $order = 'desc';
+ }🤖 Prompt for AI Agents
In `@includes/admin/outbox/class-wpsms-outbox.php` around lines 348 - 356,
Normalize and validate incoming sort params before use: run wp_unslash() and
sanitize_key() on $_REQUEST['orderby'] and sanitize_text_field/strtoupper on
$_REQUEST['order'], then check the sanitized orderby against the existing
$allowed_columns array and only use that sanitized variable in the SQL ORDER BY
string (replace direct $_REQUEST['orderby'] usage) and in the usort_reorder
comparator; also reuse the same $allowed_columns allowlist inside usort_reorder
(or pass the validated key into it) so sorting never operates on an invalid key
and you have a consistent, notice-free ordering behavior.
| /** | ||
| * The request() method returns decoded JSON directly (stdClass or array), | ||
| * not the raw WordPress HTTP response. | ||
| */ | ||
| $responseObject = $response; | ||
|
|
||
| /* | ||
| * Response validity | ||
| * Response validity - check if we got a valid response object | ||
| */ | ||
| if (wp_remote_retrieve_response_code($response) == '200') { | ||
|
|
||
| if (isset($responseObject->balance)) { | ||
| return round($responseObject->balance, 2); | ||
| } | ||
| if (isset($responseObject->balance)) { | ||
| return round($responseObject->balance, 2); | ||
| } | ||
|
|
||
| if (isset($responseObject->message)) { | ||
| return new \WP_Error('account-credit', $responseObject->message); | ||
|
|
||
| } else { | ||
| $errorResponse = isset($responseObject->message) ? $responseObject->message : $responseObject; | ||
| return new \WP_Error('account-credit', $errorResponse); | ||
| } | ||
|
|
||
| return new \WP_Error('account-credit', esc_html__('Invalid response from SMS.to API', 'wp-sms')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle array responses from request() before dereferencing
The comment says request() can return an array; accessing $responseObject->balance will fail in that case and incorrectly return “Invalid response.” Please handle both arrays and objects.
🛠️ Proposed fix
- $responseObject = $response;
+ $responseObject = $response;
+
+ $balance = is_array($responseObject)
+ ? ($responseObject['balance'] ?? null)
+ : ($responseObject->balance ?? null);
+
+ if ($balance !== null) {
+ return round((float) $balance, 2);
+ }
-
- if (isset($responseObject->balance)) {
- return round($responseObject->balance, 2);
- }
-
- if (isset($responseObject->message)) {
- return new \WP_Error('account-credit', $responseObject->message);
- }
+ $message = is_array($responseObject)
+ ? ($responseObject['message'] ?? null)
+ : ($responseObject->message ?? null);
+
+ if ($message !== null) {
+ return new \WP_Error('account-credit', $message);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * The request() method returns decoded JSON directly (stdClass or array), | |
| * not the raw WordPress HTTP response. | |
| */ | |
| $responseObject = $response; | |
| /* | |
| * Response validity | |
| * Response validity - check if we got a valid response object | |
| */ | |
| if (wp_remote_retrieve_response_code($response) == '200') { | |
| if (isset($responseObject->balance)) { | |
| return round($responseObject->balance, 2); | |
| } | |
| if (isset($responseObject->balance)) { | |
| return round($responseObject->balance, 2); | |
| } | |
| if (isset($responseObject->message)) { | |
| return new \WP_Error('account-credit', $responseObject->message); | |
| } else { | |
| $errorResponse = isset($responseObject->message) ? $responseObject->message : $responseObject; | |
| return new \WP_Error('account-credit', $errorResponse); | |
| } | |
| return new \WP_Error('account-credit', esc_html__('Invalid response from SMS.to API', 'wp-sms')); | |
| /** | |
| * The request() method returns decoded JSON directly (stdClass or array), | |
| * not the raw WordPress HTTP response. | |
| */ | |
| $responseObject = $response; | |
| /* | |
| * Response validity - check if we got a valid response object | |
| */ | |
| $balance = is_array($responseObject) | |
| ? ($responseObject['balance'] ?? null) | |
| : ($responseObject->balance ?? null); | |
| if ($balance !== null) { | |
| return round((float) $balance, 2); | |
| } | |
| $message = is_array($responseObject) | |
| ? ($responseObject['message'] ?? null) | |
| : ($responseObject->message ?? null); | |
| if ($message !== null) { | |
| return new \WP_Error('account-credit', $message); | |
| } | |
| return new \WP_Error('account-credit', esc_html__('Invalid response from SMS.to API', 'wp-sms')); |
🤖 Prompt for AI Agents
In `@includes/gateways/class-wpsms-gateway-smsto.php` around lines 178 - 195, The
balance handling assumes $responseObject is an object, but request() may return
an array; update the logic in the method in class WPSMS_Gateway_SMSTO (the block
that currently sets $responseObject = $response and then checks
$responseObject->balance / $responseObject->message) to handle both arrays and
objects: detect is_array($responseObject) and check ['balance'] and ['message']
keys (or normalize $responseObject to an object via (object)$response), convert
the balance to a float and round it, and return WP_Error('account-credit', ...)
when message exists for either type so array responses no longer cause a false
"Invalid response" error.
…ites - Use site-independent cache key that doesn't vary by home_url() - Add negative caching for failed requests (1 hour) to prevent API hammering - Fixes issue where multilingual sites (e.g., /en, /fr) generate different cache keys, causing each language variant to make separate API calls - This was causing 16,000+ daily requests from single sites and filling server disk with 88GB of fail2ban logs
- Add multisite support using get_current_blog_id() in cache key - Reduce negative cache duration from 1 hour to 5 minutes for faster retry - Add clearProductInfoCache() method for cache invalidation - Clear cache on successful license validation - Use PluginHelper::$plugins for add-on list consistency
- Add $customCacheKey parameter to RemoteRequest::execute() - Simplify ApiCommunicator to delegate caching to RemoteRequest - Keep negative cache handling for failed requests (5 min) - Reduces code duplication and improves maintainability
The TransientCacheTrait transforms cache keys via getCacheKey(), causing mismatch between negative cache (direct transient) and positive cache (trait method). Fix by using direct get_transient/ set_transient when custom cache key is provided.
Test that custom cache key uses direct transient calls instead of trait methods to avoid key transformation issues.
…17.23 build(deps): bump lodash from 4.17.21 to 4.17.23
…-4.17.23 build(deps-dev): bump lodash-es from 4.17.21 to 4.17.23
Store generated cache key in variable for reuse instead of calling generateCacheKey() twice (once for read, once for write).
Fix license API cache to prevent excessive requests on multilingual sites
- Add v7.1.1 changelog entries to CHANGELOG.md and readme.txt - Update plugin version to 7.1.1
Summary
This PR includes hardening improvements, bug fixes, and new features.
Changes
Hardening
Bug Fixes
Features
Dependencies
Documentation
Refactor
Test plan
Summary by CodeRabbit
New Features
Bug Fixes
Accessibility
Style
✏️ Tip: You can customize this high-level summary in your review settings.