--- a/give/build/adminBlocks.asset.php
+++ b/give/build/adminBlocks.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => 'ff758b5135ebdbb80f9a');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => 'dbb098c525a40bcb5325');
--- a/give/build/assets/dist/js/donor-dashboards-block.asset.php
+++ b/give/build/assets/dist/js/donor-dashboards-block.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-i18n'), 'version' => 'c3cc45f40cfb4f8764a1');
+<?php return array('dependencies' => array('react', 'wp-i18n'), 'version' => '75967e50423ec2e6d0d6');
--- a/give/build/campaignBlock.asset.php
+++ b/give/build/campaignBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-element', 'wp-i18n', 'wp-keycodes'), 'version' => '9b84dbc8e89a8fd4ef09');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-element', 'wp-i18n', 'wp-keycodes'), 'version' => '24548ad77b07c15c57f4');
--- a/give/build/campaignBlocks.asset.php
+++ b/give/build/campaignBlocks.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-keycodes', 'wp-server-side-render', 'wp-url'), 'version' => 'beea90cc574fa3364f3b');
+<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-keycodes', 'wp-server-side-render', 'wp-url'), 'version' => '6f4be7028e0299b029fc');
--- a/give/build/campaignBlocksLandingPage.asset.php
+++ b/give/build/campaignBlocksLandingPage.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-primitives', 'wp-server-side-render', 'wp-url'), 'version' => '0ca6d02a33000c0e251f');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-primitives', 'wp-server-side-render', 'wp-url'), 'version' => 'b7db9447764871579da8');
--- a/give/build/campaignCommentsBlockApp.asset.php
+++ b/give/build/campaignCommentsBlockApp.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '21440790b5124d2603f5');
+<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '5cbd3620b484b2109b61');
--- a/give/build/campaignCoverBlock.asset.php
+++ b/give/build/campaignCoverBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-primitives'), 'version' => '630d46054aa781f5f468');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-primitives'), 'version' => '8a2bec5d136d57fe053e');
--- a/give/build/campaignDonateButtonBlock.asset.php
+++ b/give/build/campaignDonateButtonBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-server-side-render', 'wp-url'), 'version' => 'bb78ff81c8d8a6ffa7ec');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-server-side-render', 'wp-url'), 'version' => 'e462129ff863e9af5928');
--- a/give/build/campaignFormBlock.asset.php
+++ b/give/build/campaignFormBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render', 'wp-url'), 'version' => '4cbf5d9a5a095b7e53d7');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render', 'wp-url'), 'version' => '21911f9a092862be7333');
--- a/give/build/campaignGoalBlock.asset.php
+++ b/give/build/campaignGoalBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n'), 'version' => '7ea3200420c98b2b2cf1');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n'), 'version' => '3519038ef2e6dfeb37d2');
--- a/give/build/donationFormBlock.asset.php
+++ b/give/build/donationFormBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => '67a4be21639c86dd01a2');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => '6af4696d4a09a13bb7ef');
--- a/give/build/elementorCampaignCommentsWidget.asset.php
+++ b/give/build/elementorCampaignCommentsWidget.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '2da34d48e655d6523d75');
+<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => 'b85453379242f6200372');
--- a/give/give.php
+++ b/give/give.php
@@ -6,7 +6,7 @@
* Description: The most robust, flexible, and intuitive way to accept donations on WordPress.
* Author: GiveWP
* Author URI: https://givewp.com/
- * Version: 4.13.1
+ * Version: 4.13.2
* Requires at least: 6.6
* Requires PHP: 7.4
* Text Domain: give
@@ -425,7 +425,7 @@
{
// Plugin version.
if (!defined('GIVE_VERSION')) {
- define('GIVE_VERSION', '4.13.1');
+ define('GIVE_VERSION', '4.13.2');
}
// Plugin Root File.
--- a/give/includes/donors/class-give-donor-wall.php
+++ b/give/includes/donors/class-give-donor-wall.php
@@ -79,42 +79,43 @@
}
- /**
- * Displays donors in a grid layout.
- *
+ /**
+ * Displays donors in a grid layout.
+ *
+ * @since 4.13.2 add strip_shortcodes to the html output
* @since 4.3.1 remove redundant _give_redirect_form_id() function.
* @since 3.7.0 Sanitize attributes
* @since 2.27.0 Moved AJAX nonce verification to ajax_handler method.
- * @since 2.2.0
- *
- * @param array $atts {
- * Optional. Attributes of the donor wall shortcode.
- *
- * @type int $donors_per_page Number of donors per page. Default '20'.
- * @type int $form_id The donation form to filter donors by. Default is all forms (no filter).
- * @type bool $paged Whether to paginate donors. Default 'true'.
- * @type string $ids A comma-separated list of donor IDs to display. Default empty.
- * @type string $columns Maximum columns to display. Default 'best-fit'.
- * Accepts 'best-fit', '1', '2', '3', '4'.
- * @type bool $show_avatar Whether to display the donor's gravatar image if available. Default 'true'.
- * @type bool $show_name Whether to display the donor's full name, first and last. Default 'true'.
- * @type bool $show_company_name Whether to display the donor's company name. Default 'false'.
- * @type bool $show_total Whether to display the donor's donation amount. Default 'true'.
- * @type bool $show_comments Whether to display the donor's comment if they left one. Default 'true'.
- * @type int $comment_length The number of words to display for the comments before a "Read more" field
- * @type int $only_comments Whether to display the donors only with comment. Default 'false'.
+ * @since 2.2.0
+ *
+ * @param array $atts {
+ * Optional. Attributes of the donor wall shortcode.
+ *
+ * @type int $donors_per_page Number of donors per page. Default '20'.
+ * @type int $form_id The donation form to filter donors by. Default is all forms (no filter).
+ * @type bool $paged Whether to paginate donors. Default 'true'.
+ * @type string $ids A comma-separated list of donor IDs to display. Default empty.
+ * @type string $columns Maximum columns to display. Default 'best-fit'.
+ * Accepts 'best-fit', '1', '2', '3', '4'.
+ * @type bool $show_avatar Whether to display the donor's gravatar image if available. Default 'true'.
+ * @type bool $show_name Whether to display the donor's full name, first and last. Default 'true'.
+ * @type bool $show_company_name Whether to display the donor's company name. Default 'false'.
+ * @type bool $show_total Whether to display the donor's donation amount. Default 'true'.
+ * @type bool $show_comments Whether to display the donor's comment if they left one. Default 'true'.
+ * @type int $comment_length The number of words to display for the comments before a "Read more" field
+ * @type int $only_comments Whether to display the donors only with comment. Default 'false'.
* @type bool $show_time Whether to display date of the last donation. Default 'true'.
- *
- * @type string $readmore_text Link label for modal in which donor can read full comment.
- * @type string $loadmore_text Button label which will load more donor comments.
- * @type int $avatar_size Avatar image size in pixels without the "px". Default "75"
- * @type string $orderby The order in which you want the donations to appear.
- * Currently we are using this attribute internally and, it will sort donations by created date.
- * @type string $order The order in which you want the donors to appear. Accepts "ASC". "DESC".
- *
- * }
- * @return string|bool The markup of the form grid or false.
- */
+ *
+ * @type string $readmore_text Link label for modal in which donor can read full comment.
+ * @type string $loadmore_text Button label which will load more donor comments.
+ * @type int $avatar_size Avatar image size in pixels without the "px". Default "75"
+ * @type string $orderby The order in which you want the donations to appear.
+ * Currently we are using this attribute internally and, it will sort donations by created date.
+ * @type string $order The order in which you want the donors to appear. Accepts "ASC". "DESC".
+ *
+ * }
+ * @return string|bool The markup of the form grid or false.
+ */
public function render_shortcode( $atts ) {
$atts = give_clean($atts);
@@ -145,8 +146,11 @@
$html = ob_get_clean();
- // Return only donor html.
- if (
+ // Strip shortcodes to prevent execution of user-supplied shortcode syntax.
+ $html = strip_shortcodes($html);
+
+ // Return only donor html.
+ if (
isset( $atts['only_donor_html'] )
&& wp_doing_ajax()
&& $atts['only_donor_html']
--- a/give/src/Campaigns/Actions/RegisterCampaignBlocks.php
+++ b/give/src/Campaigns/Actions/RegisterCampaignBlocks.php
@@ -39,6 +39,10 @@
*/
public function loadBlockEditorAssets(): void
{
+ if (!is_admin()) {
+ return;
+ }
+
give(LoadCampaignAdminOptions::class)();
$handleName = 'givewp-campaign-blocks';
--- a/give/src/Campaigns/Blocks/CampaignDonations/resources/views/render.php
+++ b/give/src/Campaigns/Blocks/CampaignDonations/resources/views/render.php
@@ -90,7 +90,7 @@
<?php
printf(
__('%s donated %s', 'give'),
- '<strong>' . esc_html(!$donation->isAnonymous ? $donation->donorName : __('Anonymous', 'give')) . '</strong>',
+ '<strong>' . esc_html(!$donation->isAnonymous ? strip_shortcodes($donation->donorName) : __('Anonymous', 'give')) . '</strong>',
'<strong>' . esc_html($donation->amount->formatToLocale()) . '</strong>'
);
?>
--- a/give/src/Campaigns/Blocks/CampaignDonors/resources/views/render.php
+++ b/give/src/Campaigns/Blocks/CampaignDonors/resources/views/render.php
@@ -93,7 +93,7 @@
<div class="givewp-campaign-donors-block__donor-info">
<span class="givewp-campaign-donors-block__donor-name"><?php
- echo esc_html(!$donor->isAnonymous ? $donor->name : __('Anonymous', 'give')); ?></span>
+ echo esc_html(!$donor->isAnonymous ? strip_shortcodes($donor->name) : __('Anonymous', 'give')); ?></span>
<?php
if ($sortBy === 'top-donors' && $key < 3) : ?>
--- a/give/src/Campaigns/ServiceProvider.php
+++ b/give/src/Campaigns/ServiceProvider.php
@@ -216,7 +216,7 @@
Hooks::addAction('rest_api_init', ActionsRegisterCampaignIdRestField::class);
Hooks::addAction('init', ActionsRegisterCampaignBlocks::class);
- Hooks::addAction('enqueue_block_editor_assets', ActionsRegisterCampaignBlocks::class, 'loadBlockEditorAssets');
+ Hooks::addAction('enqueue_block_assets', ActionsRegisterCampaignBlocks::class, 'loadBlockEditorAssets');
Hooks::addAction('init', ActionsRegisterCampaignShortcodes::class);
}
--- a/give/src/PaymentGateways/PayPalCommerce/AdminSettingFields.php
+++ b/give/src/PaymentGateways/PayPalCommerce/AdminSettingFields.php
@@ -313,17 +313,20 @@
<?php if ($this->merchantRepository->accountIsConnected()) :?>
<?php
- $reCheckAccountStatusUrl = add_query_arg(
- [
- 'post_type' => 'give_forms',
- 'page' => 'give-settings',
- 'tab' => 'gateways',
- 'section' => 'paypal',
- 'group' => 'paypal-commerce',
- 'paypalStatusCheck' => '1',
- 'mode' => $merchantDetailsRepository->getMode()
- ],
- admin_url('edit.php')
+ $reCheckAccountStatusUrl = wp_nonce_url(
+ add_query_arg(
+ [
+ 'post_type' => 'give_forms',
+ 'page' => 'give-settings',
+ 'tab' => 'gateways',
+ 'section' => 'paypal',
+ 'group' => 'paypal-commerce',
+ 'paypalStatusCheck' => '1',
+ 'mode' => $merchantDetailsRepository->getMode()
+ ],
+ admin_url('edit.php')
+ ),
+ 'give_paypal_status_refresh'
);
?>
<p>
--- a/give/src/PaymentGateways/PayPalCommerce/AjaxRequestHandler.php
+++ b/give/src/PaymentGateways/PayPalCommerce/AjaxRequestHandler.php
@@ -2,7 +2,6 @@
namespace GivePaymentGatewaysPayPalCommerce;
-use GiveFrameworkHttpConnectServerClientConnectClient;
use GivePaymentGatewaysPayPalCommerceModelsMerchantDetail;
use GivePaymentGatewaysPayPalCommercePayPalCheckoutSdkProcessorResponseError;
use GivePaymentGatewaysPayPalCommerceRepositoriesMerchantDetails;
@@ -51,7 +50,7 @@
/**
* @since 2.9.0
*
- * @var ConnectClient
+ * @var RefreshToken
*/
private $refreshToken;
@@ -157,12 +156,18 @@
$country = sanitize_text_field(wp_unslash($_GET['countryCode']));
$accountType = sanitize_text_field(wp_unslash($_GET['accountType']));
$mode = sanitize_text_field(wp_unslash($_GET['mode']));
+
+ // Generate a unique state token for CSRF protection on PayPal callback.
+ $stateToken = wp_generate_password(32, false);
+ set_transient('give_paypal_onboarding_state_' . $mode, $stateToken, HOUR_IN_SECONDS);
+
$redirectUrl = add_query_arg(
[
'tab' => 'gateways',
'section' => 'paypal',
'group' => 'paypal-commerce',
'mode' => $mode,
+ 'give_paypal_state' => $stateToken,
],
admin_url('edit.php?post_type=give_forms&page=give-settings')
);
--- a/give/src/PaymentGateways/PayPalCommerce/onBoardingRedirectHandler.php
+++ b/give/src/PaymentGateways/PayPalCommerce/onBoardingRedirectHandler.php
@@ -215,6 +215,7 @@
/**
* Redirects the user to the account connected url
*
+ * @since 4.13.2 Add nonce to redirect URL for CSRF protection.
* @since 2.9.0
*/
private function redirectAccountConnected()
@@ -222,16 +223,19 @@
$this->refreshAccountStatus();
wp_redirect(
- add_query_arg(
- [
- 'post_type' => 'give_forms',
- 'page' => 'give-settings',
- 'tab' => 'gateways',
- 'section' => 'paypal',
- 'group' => 'paypal-commerce',
- 'paypal-commerce-account-connected' => '1'
- ],
- admin_url('edit.php')
+ wp_nonce_url(
+ add_query_arg(
+ [
+ 'post_type' => 'give_forms',
+ 'page' => 'give-settings',
+ 'tab' => 'gateways',
+ 'section' => 'paypal',
+ 'group' => 'paypal-commerce',
+ 'paypal-commerce-account-connected' => '1'
+ ],
+ admin_url('edit.php')
+ ),
+ 'give_paypal_account_connected'
)
);
@@ -294,40 +298,71 @@
/**
* Returns whether or not the current request is for refreshing the account status
*
+ * @since 4.13.2 Add nonce verification for CSRF protection.
* @since 2.9.0
*
* @return bool
*/
private function isStatusRefresh()
{
- return isset($_GET['paypalStatusCheck']) && Give_Admin_Settings::is_setting_page('gateways', 'paypal');
+ if (!isset($_GET['paypalStatusCheck']) || !Give_Admin_Settings::is_setting_page('gateways', 'paypal')) {
+ return false;
+ }
+
+ if (!isset($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'give_paypal_status_refresh')) {
+ return false;
+ }
+
+ return true;
}
/**
* Return whether or not PayPal user redirect to GiveWP setting page after successful onboarding.
*
+ * @since 4.13.2 Add state parameter verification for CSRF protection.
* @since 2.9.0
*
* @return bool
*/
private function isPayPalUserRedirected()
{
- return isset($_GET['merchantIdInPayPal']) && Give_Admin_Settings::is_setting_page('gateways', 'paypal');
+ if (!isset($_GET['merchantIdInPayPal']) || !Give_Admin_Settings::is_setting_page('gateways', 'paypal')) {
+ return false;
+ }
+
+ // Verify the state parameter to protect against CSRF attacks.
+ $mode = isset($_GET['mode']) && in_array($_GET['mode'], ['live', 'sandbox'], true) ? $_GET['mode'] : 'live';
+ $storedState = get_transient('give_paypal_onboarding_state_' . $mode);
+
+ if (!isset($_GET['give_paypal_state']) || !$storedState || !hash_equals($storedState, $_GET['give_paypal_state'])) {
+ return false;
+ }
+
+ // Delete the transient after successful verification to prevent replay attacks.
+ delete_transient('give_paypal_onboarding_state_' . $mode);
+
+ return true;
}
/**
* Return whether or not PayPal account details saved.
*
+ * @since 4.13.2 Add nonce verification for CSRF protection.
* @since 2.9.0
*
* @return bool
*/
private function isPayPalAccountDetailsSaved()
{
- return isset($_GET['paypal-commerce-account-connected']) && Give_Admin_Settings::is_setting_page(
- 'gateways',
- 'paypal'
- );
+ if (!isset($_GET['paypal-commerce-account-connected']) || !Give_Admin_Settings::is_setting_page('gateways', 'paypal')) {
+ return false;
+ }
+
+ if (!isset($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'give_paypal_account_connected')) {
+ return false;
+ }
+
+ return true;
}
/**
--- a/give/vendor/composer/installed.php
+++ b/give/vendor/composer/installed.php
@@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'name' => 'impress-org/give',
- 'pretty_version' => '4.13.1',
- 'version' => '4.13.1.0',
- 'reference' => '2e6bcbac315d9b736383a564bfbd99d6f38a1e95',
+ 'pretty_version' => '4.13.2',
+ 'version' => '4.13.2.0',
+ 'reference' => 'deb5f3cda042959ada2445c0b65981ab014edff0',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -20,9 +20,9 @@
'dev_requirement' => false,
),
'impress-org/give' => array(
- 'pretty_version' => '4.13.1',
- 'version' => '4.13.1.0',
- 'reference' => '2e6bcbac315d9b736383a564bfbd99d6f38a1e95',
+ 'pretty_version' => '4.13.2',
+ 'version' => '4.13.2.0',
+ 'reference' => 'deb5f3cda042959ada2445c0b65981ab014edff0',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),