1.6.0: a shipping method a group is shown belongs to that group
Kristian checked out with an address in no group and was offered both free delivery methods set up for the Netic group. Measured on the shop before touching anything: one group, four rules, two of them "show" on the free delivery methods he saw. It was not a regression. A test asserted the behaviour as intended — "an unmatched customer sees every method" — so a "show" rule was a whitelist FOR THE GROUP and nothing more. The plugin did half of what its own description promises: it filtered what the group sees and never reserved anything from anyone else. On a shop that takes money the effect is that free delivery arranged for one company is available to every customer. A method named in any group's show rules is now reserved for the groups that name it. Answering "does it work for other discounts too", which found the hole the first fix left: it did not. A group with NO rules of its own returned every rate untouched, and so did a group carrying only "hide" rules, because neither engages the whitelist — so their members saw another group's reserved methods. Both now have other groups' reserved methods removed. The "never leave the customer without shipping" guard is kept and asserted: a shop that reserved everything it offers must still let an ordinary customer check out, because that failure is worse than the leak. Four assertions, each proven red. Two of them caught my own mistakes rather than the product's: the hide-only fixture originally left nothing for the customer, so the guard fired and returned every rate — which reads exactly like the leak it was written to detect. A third rate makes the behaviour observable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d9de751e3f
commit
6ef4ee8fe5
+70
-1
@@ -194,9 +194,78 @@ WC()->customer->set_billing_email( 'someone@company.com' );
|
||||
$filtered = wc_edm()->frontend->filter_shipping_rates( $rates, array() );
|
||||
wc_edm_is( 'a "show" rule hides every method it does not name', array( 'flat_rate:1' ), array_keys( $filtered ) );
|
||||
|
||||
// 🔴 This used to assert "an unmatched customer sees every method", which is
|
||||
// what made a reserved free-delivery method visible to everybody on a live
|
||||
// shop. A "show" rule now means the method belongs to that group.
|
||||
WC()->customer->set_billing_email( 'nobody@nowhere.test' );
|
||||
$filtered = wc_edm()->frontend->filter_shipping_rates( $rates, array() );
|
||||
wc_edm_is( 'an unmatched customer sees every method', 2, count( $filtered ) );
|
||||
wc_edm_is(
|
||||
'an unmatched customer does not see a method a group reserved',
|
||||
array( 'local_pickup:2' ),
|
||||
array_keys( $filtered )
|
||||
);
|
||||
|
||||
// The guard that keeps the fix from being worse than the leak: a shop that
|
||||
// reserved everything it offers must still let an ordinary customer check out.
|
||||
WC_EDM_DB::replace_shipping_rules(
|
||||
(int) $fixtures['group_id'],
|
||||
array(
|
||||
array( 'shipping_method_id' => 'flat_rate:1', 'action' => 'show', 'sort_order' => 0 ),
|
||||
array( 'shipping_method_id' => 'local_pickup:2', 'action' => 'show', 'sort_order' => 1 ),
|
||||
)
|
||||
);
|
||||
$filtered = wc_edm()->frontend->filter_shipping_rates( $rates, array() );
|
||||
wc_edm_is( 'but is never left with no shipping at all', 2, count( $filtered ) );
|
||||
|
||||
WC_EDM_DB::replace_shipping_rules(
|
||||
(int) $fixtures['group_id'],
|
||||
array( array( 'shipping_method_id' => 'flat_rate:1', 'action' => 'show', 'sort_order' => 0 ) )
|
||||
);
|
||||
|
||||
// A SECOND group, with no rules of its own. Its members must not see what the
|
||||
// first group reserved — the leak was reported for customers matching nothing,
|
||||
// but it existed for this shape too, and for a group carrying only "hide"
|
||||
// rules, because neither engages the whitelist.
|
||||
$wc_edm_other = WC_EDM_DB::insert_group( array( 'name' => 'Ruleless', 'discount_percent' => 5 ) );
|
||||
WC_EDM_DB::insert_email(
|
||||
array(
|
||||
'email_pattern' => 'ruleless@elsewhere.test',
|
||||
'assignment_type' => 'group',
|
||||
'group_id' => (int) $wc_edm_other,
|
||||
'coupon_code' => null,
|
||||
'custom_discount' => null,
|
||||
'is_active' => 1,
|
||||
'free_shipping' => 0,
|
||||
)
|
||||
);
|
||||
|
||||
WC()->customer->set_billing_email( 'ruleless@elsewhere.test' );
|
||||
$filtered = wc_edm()->frontend->filter_shipping_rates( $rates, array() );
|
||||
wc_edm_is(
|
||||
'a group with no rules still does not see another group reserved method',
|
||||
array( 'local_pickup:2' ),
|
||||
array_keys( $filtered )
|
||||
);
|
||||
|
||||
// A third rate, deliberately: with only two, hiding one and reserving the
|
||||
// other leaves nothing, the "never leave the customer without shipping" guard
|
||||
// fires, and every rate comes back — which reads as a leak but is the guard
|
||||
// working. The fixture has to leave something for the customer to be given.
|
||||
$wc_edm_rates3 = $rates;
|
||||
$wc_edm_rates3['flat_rate:9'] = new WC_Shipping_Rate( 'flat_rate:9', 'Another flat rate', 12, array(), 'flat_rate' );
|
||||
|
||||
WC_EDM_DB::replace_shipping_rules(
|
||||
(int) $wc_edm_other,
|
||||
array( array( 'shipping_method_id' => 'local_pickup:2', 'action' => 'hide', 'sort_order' => 0 ) )
|
||||
);
|
||||
$filtered = wc_edm()->frontend->filter_shipping_rates( $wc_edm_rates3, array() );
|
||||
wc_edm_ok(
|
||||
'and neither does a group carrying only hide rules',
|
||||
! in_array( 'flat_rate:1', array_keys( $filtered ), true ),
|
||||
'saw: ' . implode( ', ', array_keys( $filtered ) )
|
||||
);
|
||||
|
||||
WC()->customer->set_billing_email( 'someone@company.com' );
|
||||
|
||||
WC_EDM_DB::replace_shipping_rules( (int) $fixtures['group_id'], array() );
|
||||
|
||||
|
||||
Reference in New Issue
Block a user