GitHub Advisoryadvisory · Packagist

GHSA-hxg4-65p5-9w37 Sylius PayPal Plugin has an Order Manipulation Vulnerability after PayPal Checkout

MEDIUM Published 2025-03-19 Modified 2025-03-20 Official page ↗
Aliases
CVE-2025-30152
Ecosystem
Packagist
Packages
sylius/paypal-plugin
Severity
Moderate
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Weaknesses
CWE-472
Reviewed
2025-03-19

Affected packages

• Packagist sylius/paypal-plugin: < 1.6.2 → fixed in 1.6.2 • Packagist sylius/paypal-plugin: >= 1.7.0, < 1.7.2 → fixed in 1.7.2 • Packagist sylius/paypal-plugin: >= 2.0.0, < 2.0.2 → fixed in 2.0.2

Description

A discovered vulnerability allows users to modify their shopping cart after completing the PayPal Checkout process and payment authorization. If a user initiates a PayPal transaction from a product page or the cart page and then returns to the order summary page, they can still manipulate the cart contents before finalizing the order. As a result, the order amount in Sylius may be higher than the amount actually captured by PayPal, leading to a scenario where merchants deliver products or services without full payment. Impact • Users can exploit this flaw to receive products/services without paying the full amount. • Merchants may suffer financial losses due to underpaid orders. • Trust in the integrity of the payment process is compromised. Patches The issue is fixed in versions: 1.6.2, 1.7.2, 2.0.2 and above. Workarounds To resolve the problem in the end application without updating to the newest patches, there is a need to overwrite `PayPalOrderCompleteProcessor` with modified logic: ```php getLastPayment(PaymentInterface::STATE_PROCESSING); if ($payment === null) { return; } /** @var PaymentMethodInterface $paymentMethod */ $paymentMethod = $payment->getMethod(); /** @var GatewayConfigInterface $gatewayConfig */ $gatewayConfig = $paymentMethod->getGatewayConfig(); if ($gatewayConfig->getFactoryName() !== 'sylius.pay_pal') { return; } try { $this->verify($payment); } catch (\Exception) { $this->paymentStateManager->cancel($payment); return; } $this->paymentStateManager->complete($payment); } private function verify(PaymentInterface $payment): void { $totalAmount = $this->getTotalPaymentAmountFromPaypal($payment); if ($payment->getOrder()->getTotal() !== $totalAmount) { throw new \Exception(); } } private function getTotalPaymentAmountFromPaypal(PaymentInterface $payment): int { $details = $payment->getDetails(); return $details['payment_amount'] ?? 0; } } ``` IMPORTANT For `PayPalPlugin 2.x` change: ```php $gatewayConfig->getFactoryName() !== 'sylius.pay_pal' ``` to ```php $gatewayConfig->getFactoryName() !== SyliusPayPalExtension::PAYPAL_FACTORY_NAME ``` Also there is a need to overwrite `CompletePayPalOrderListener` with modified logic: ```php getSubject(); Assert::isInstanceOf($order, OrderInterface::class); $this->completeProcessor->completePayPalOrder($order); } } ``` And to overwrite `CaptureAction` with modified logic (if you didn't have it already): ```php getModel(); /** @var PaymentMethodInterface $paymentMethod */ $paymentMethod = $payment->getMethod(); $token = $this->authorizeClientApi->authorize($paymentMethod); $referenceId = $this->uuidProvider->provide(); $content = $this->createOrderApi->create($token, $payment, $referenceId); if ($content['status'] === 'CREATED') { $payment->setDetails([ 'status' => StatusAction::STATUS_CAPTURED, 'paypal_order_id' => $content['id'], 'reference_id' => $referenceId, 'payment_amount' => $payment->getAmount(), ]); } } public function supports($request): bool { return $request instanceof Capture && $request->getModel() instanceof PaymentInterface ; } } ``` After that, register services in the container when using PayPal 1.x: ```yaml Sylius\PayPalPlugin\EventListener\Workflow\CompletePayPalOrderListener: class: App\EventListener\Workflow\CompletePayPalOrderListener public: true arguments: • '@Sylius\PayPalPlugin\Processor\PayPalOrderCompleteProcessor' tags: • { name: 'kernel.event_listener', event: 'workflow.sylius_order_checkout.completed.complete', priority: 100 } Sylius\PayPalPlugin\Processor\PayPalOrderCompleteProcessor: class: App\Processor\PayPalOrderCompleteProcessor public: true arguments: • '@Sylius\PayPalPlugin\Manager\PaymentStateManagerInterface' Sylius\PayPalPlugin\Payum\Action\CaptureAction: class: App\Payum\Action\CaptureAction public: true arguments: • '@Sylius\PayPalPlugin\Api\CacheAuthorizeClientApiInterface' • '@Sylius\PayPalPlugin\Api\CreateOrderApiInterface' • '@Sylius\PayPalPlugin\Provider\UuidProviderInterface' tags: • { name: 'payum.action', factory: 'sylius.pay_pal', alias: 'payum.action.capture' } ``` or when using PayPal 2.x: ```yaml sylius_paypal.listener.workflow.complete_paypal_order: class: App\EventListener\Workflow\CompletePayPalOrderListener public: true arguments: • '@sylius_paypal.processor.paypal_order_complete' tags: • { name: 'kernel.event_listener', event: 'workflow.sylius_order_checkout.completed.complete', priority: 100 } sylius_paypal.processor.paypal_order_complete: class: App\Processor\PayPalOrderCompleteProcessor public: true arguments: • '@sylius_paypal.manager.payment_state' sylius_paypal.payum.action.capture: class: App\Payum\Action\CaptureAction public: true arguments: • '@sylius_paypal.api.cache_authorize_client' • '@sylius_paypal.api.create_order' • '@sylius_paypal.provider.uuid' tags: • { name: 'payum.action', factory: 'sylius.paypal', alias: 'payum.action.capture' } ``` For more information If you have any questions or comments about this advisory: • Open an issue in Sylius issues • Email us at [email protected]

Related

Alias · CVE (1)

  • CVE-2025-30152 The Syliud PayPal Plugin is the Sylius Core Team’s plugin for the PayPal Commerce Platform. vulnerability

Weakness · CWE (1)

  • CWE-472 External Control of Assumed-Immutable Web Parameter weakness

References