{"id":"CVE-2026-56831","aliases":["EUVD-2026-78917","GHSA-5vf4-452p-jjhf"],"status":"published","title":"Shopper: Negative discount values accepted and propagated through order calculation pipeline","description":"Shopper is a Headless e-commerce Admin Panel. Prior to 2.9.0, the /cpanel/discounts administrative interface accepts negative fixed_amount discount values, persists them in sh_discounts, and passes them through vendor/shopper/cart/src/Discounts/DiscountCalculator.php and vendor/shopper/cart/src/Pipelines/Calculate.php without enforcing a positive-value invariant. Because the calculation subtracts discountTotal from the subtotal, a negative discount increases the resulting order total instead of reducing it. Malformed discount records can therefore cause incorrect pricing and financial data integrity failures, although the advisory does not establish a customer-facing exploitation path. This issue is fixed in version 2.9.0.","severity":{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00435,"percentile":0.38269,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-20"],"published":"2026-09-11T21:28:00Z","modified":"2026-10-28T02:30:42Z","fixes":[{"ecosystem":"Packagist","package":"shopper/framework","fixed_in":["2.9.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"shopper/framework","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.9.0"}]}]},{"source":"osv","ecosystem":"Packagist","package":"shopper/framework","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.9.0"}]}],"versions":["v2.0.0","v2.0.0-alpha","v2.0.0-beta","v2.0.0-beta10","v2.0.0-beta11","v2.0.0-beta12","v2.0.0-beta13","v2.0.0-beta14","v2.0.0-beta15","v2.0.0-beta16","v2.0.0-beta17","v2.0.0-beta18","v2.0.0-beta19","v2.0.0-beta2","v2.0.0-beta20","v2.0.0-beta21","v2.0.0-beta3","v2.0.0-beta4","v2.0.0-beta5","v2.0.0-beta6","v2.0.0-beta7","v2.0.0-beta8","v2.0.0-beta9","v2.0.1","v2.0.2","v2.0.3","v2.1.1","v2.1.2","v2.1.3","v2.1.4","v2.1.5","v2.1.6","v2.2","v2.2.1","v2.2.2","v2.2.3","v2.2.4","v2.2.5","v2.2.6","v2.2.7","v2.3","v2.3.1","v2.3.2","v2.3.3","v2.4.0","v2.4.1","v2.4.2","v2.4.3","v2.5.0","v2.5.1","v2.6.0","v2.6.1","v2.6.2","v2.6.3","v2.6.4","v2.7.0","v2.7.1","v2.7.2","v2.7.3","v2.8.0","v2.8.1"]}],"products":[{"vendor":"shopperdotcom","product":"shopper"},{"vendor":"shopperlabs","product":"shopper"}],"references":[{"url":"https://github.com/shopperlabs/shopper/security/advisories/GHSA-5vf4-452p-jjhf","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/shopperlabs/shopper/pull/528","tags":["x_refsource_MISC","fix"]},{"url":"https://github.com/shopperlabs/shopper/commit/967e616281ded1a0050d9102858ecd7dd7f66a41","tags":["x_refsource_MISC","fix"]},{"url":"https://github.com/shopperlabs/shopper/releases/tag/v2.9.0","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/advisories/GHSA-5vf4-452p-jjhf"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/56xxx/CVE-2026-56831.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-56831","tags":["advisory"]},{"url":"https://github.com/shopperlabs/shopper","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-56831","title":"Shopper: Negative discount values accepted and propagated through order calculation pipeline","description":"Shopper is a Headless e-commerce Admin Panel. Prior to 2.9.0, the /cpanel/discounts administrative interface accepts negative fixed_amount discount values, persists them in sh_discounts, and passes them through vendor/shopper/cart/src/Discounts/DiscountCalculator.php and vendor/shopper/cart/src/Pipelines/Calculate.php without enforcing a positive-value invariant. Because the calculation subtracts discountTotal from the subtotal, a negative discount increases the resulting order total instead of reducing it. Malformed discount records can therefore cause incorrect pricing and financial data integrity failures, although the advisory does not establish a customer-facing exploitation path. This issue is fixed in version 2.9.0.","status":"published","published":"2026-09-15T17:42:09.495Z","modified":"2026-09-16T14:41:52.114Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T13:36:51.6229Z"}],"euvd":[{"id":"EUVD-2026-78917","description":"Shopper is a Headless e-commerce Admin Panel. Prior to 2.9.0, the /cpanel/discounts administrative interface accepts negative fixed_amount discount values, persists them in sh_discounts, and passes them through vendor/shopper/cart/src/Discounts/DiscountCalculator.php and vendor/shopper/cart/src/Pipelines/Calculate.php without enforcing a positive-value invariant. Because the calculation subtracts discountTotal from the subtotal, a negative discount increases the resulting order total instead of reducing it. Malformed discount records can therefore cause incorrect pricing and financial data integrity failures, although the advisory does not establish a customer-facing exploitation path. This issue is fixed in version 2.9.0.","status":"published","published":"2026-09-15T17:42:09Z","modified":"2026-09-16T14:41:52Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-02T17:28:15.854171Z"}],"ghsa":[{"id":"GHSA-5vf4-452p-jjhf","title":"Shopper: Negative discount values accepted and propagated through order calculation pipeline","description":"## Summary\n\nThe Shopper Framework discount management functionality accepts negative discount values without server-side validation.\n\nIt was confirmed that negative fixed-amount discounts can be created through the administrative interface, persisted to the database, and subsequently processed by the cart/order calculation pipeline.\n\nThe application appears to assume that discount values are always positive but does not enforce this assumption during creation, storage, or calculation.\n\nAs a result, malformed discount records can influence financial calculations and produce unintended order totals.\n\n---\n\n## Affected Product\n\n**Package:** shopper/framework\n\n**Version Tested:** 2.8.1\n\n---\n\n## Vulnerability Type\n\n* Business Logic Vulnerability\n* Improper Input Validation (CWE-20)\n\n---\n\n## Description\n\nWhile reviewing the discount functionality, it was discovered that the application accepts negative discount values through the administrative interface.\n\nExample values tested:\n\n```text\n-50.00\n-99,999,999.00\n```\n\nThe application accepted these values without validation and stored them in the database.\n\nExample records observed in the `sh_discounts` table:\n\n```text\n1 | QCZ5Y3HESM | fixed_amount | -5000\n4 | TOZKAHCB4S | fixed_amount | -9999999900\n```\n\nThis demonstrates that negative discount values are successfully persisted.\n\n---\n\n## Steps to Reproduce\n\n### 1. Create a Discount\n\nLogin as an administrator.\n\nNavigate to:\n\n```text\n/cpanel/discounts\n```\n\nCreate a new discount with the following values:\n\n```text\nType: fixed_amount\nValue: -99999999\n```\n\nSave the discount.\n\n### 2. Observe Successful Creation\n\nThe discount is accepted by the application and displayed in the administration interface.\n\nExample:\n\n```text\nCode: TOZKAHCB4S\nAmount: -$99,999,999.00\n```\n\n### 3. Verify Database Persistence\n\nInspect the database:\n\n```sql\nselect * from sh_discounts;\n```\n\nObserved entry:\n\n```text\nTOZKAHCB4S | fixed_amount | -9999999900\n```\n\n---\n\n## Technical Analysis\n\n### Discount Calculation\n\nFile:\n\n```text\nvendor/shopper/cart/src/Discounts/DiscountCalculator.php\n```\n\nObserved code:\n\n```php\n$fixedAmount = $discount-\u003evalue;\n```\n\nThe value is later processed without validation:\n\n```php\n$fixedAmount = min($fixedAmount, $applicableSubtotal);\n```\n\nWhen a negative value is supplied:\n\n```php\nmin(-9999999900, 10000)\n```\n\nreturns:\n\n```php\n-9999999900\n```\n\nallowing the negative value to continue through the calculation pipeline.\n\nThe resulting adjustment values are inserted into the database:\n\n```php\nCartLineAdjustment::query()-\u003einsert($adjustments);\n```\n\nNo validation was identified to ensure that discount amounts are positive before calculations occur.\n\n---\n\n### Final Total Calculation\n\nFile:\n\n```text\nvendor/shopper/cart/src/Pipelines/Calculate.php\n```\n\nObserved logic:\n\n```php\n$context-\u003etotal = max(\n    0,\n    $context-\u003etaxInclusive\n        ? $context-\u003esubtotal - $context-\u003ediscountTotal\n        : $context-\u003esubtotal - $context-\u003ediscountTotal + $context-\u003etaxTotal\n);\n```\n\nBecause negative discount values are allowed to reach this stage, financial calculations are performed using malformed discount data.\n\nExample:\n\n```text\nSubtotal      = 10000\nDiscountTotal = -5000\n```\n\nResulting calculation:\n\n```text\n10000 - (-5000)\n```\n\nResult:\n\n```text\n15000\n```\n\nThis demonstrates that negative discount values directly affect order total calculations.\n\n---\n\n## Impact\n\nThe following was confirmed:\n\n* Negative discount values are accepted.\n* Negative discount values are persisted.\n* Negative discount values are processed by the discount calculation engine.\n* Negative discount values affect order total calculations.\n\nPotential consequences include:\n\n* Incorrect pricing calculations.\n* Financial data integrity issues.\n* Unexpected order totals.\n* Violated assumptions within downstream pricing logic.\n* Future vulnerabilities if additional components assume discount values are always positive.\n\nBecause Shopper is a headless e-commerce administration framework and does not ship with a customer-facing storefront, it was not verified a customer-facing exploitation path.\n\nHowever, malformed discount records currently propagate through pricing calculations without validation.\n\n---\n\n## Recommendation\n\nImplement server-side validation enforcing positive discount values before persistence and before entering the calculation pipeline.\n\nSuggested validation:\n\n### Fixed Amount Discounts\n\n```text\nvalue \u003e 0\n```\n\n### Percentage Discounts\n\n```text\n0 \u003c value \u003c= 100\n```\n\nAdditionally, existing discount records should be validated before calculation to prevent malformed data from influencing pricing logic.\n\n---\n\n## Environment\n\n```text\nShopper Framework 2.8.1\nLaravel 12.61.1\nPHP 8.4.16\nSQLite\n```","status":"published","published":"2026-09-11T21:28:00Z","modified":"2026-09-11T21:28:01Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T14:41:15.803194Z"}],"nvd":[{"id":"CVE-2026-56831","description":"Shopper is a Headless e-commerce Admin Panel. Prior to 2.9.0, the /cpanel/discounts administrative interface accepts negative fixed_amount discount values, persists them in sh_discounts, and passes them through vendor/shopper/cart/src/Discounts/DiscountCalculator.php and vendor/shopper/cart/src/Pipelines/Calculate.php without enforcing a positive-value invariant. Because the calculation subtracts discountTotal from the subtotal, a negative discount increases the resulting order total instead of reducing it. Malformed discount records can therefore cause incorrect pricing and financial data integrity failures, although the advisory does not establish a customer-facing exploitation path. This issue is fixed in version 2.9.0.","status":"published","published":"2026-09-15T18:17:25.213Z","modified":"2026-09-30T17:51:56.193Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T14:12:47.668237Z"}],"osv":[{"id":"CVE-2026-56831","title":"Shopper: Negative discount values accepted and propagated through order calculation pipeline","description":"Shopper is a Headless e-commerce Admin Panel. Prior to 2.9.0, the /cpanel/discounts administrative interface accepts negative fixed_amount discount values, persists them in sh_discounts, and passes them through vendor/shopper/cart/src/Discounts/DiscountCalculator.php and vendor/shopper/cart/src/Pipelines/Calculate.php without enforcing a positive-value invariant. Because the calculation subtracts discountTotal from the subtotal, a negative discount increases the resulting order total instead of reducing it. Malformed discount records can therefore cause incorrect pricing and financial data integrity failures, although the advisory does not establish a customer-facing exploitation path. This issue is fixed in version 2.9.0.","status":"published","published":"2026-09-15T17:42:09.495Z","modified":"2026-10-28T02:30:42.64795949Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T15:45:12.016252Z"},{"id":"GHSA-5vf4-452p-jjhf","title":"Shopper: Negative discount values accepted and propagated through order calculation pipeline","description":"## Summary\n\nThe Shopper Framework discount management functionality accepts negative discount values without server-side validation.\n\nIt was confirmed that negative fixed-amount discounts can be created through the administrative interface, persisted to the database, and subsequently processed by the cart/order calculation pipeline.\n\nThe application appears to assume that discount values are always positive but does not enforce this assumption during creation, storage, or calculation.\n\nAs a result, malformed discount records can influence financial calculations and produce unintended order totals.\n\n---\n\n## Affected Product\n\n**Package:** shopper/framework\n\n**Version Tested:** 2.8.1\n\n---\n\n## Vulnerability Type\n\n* Business Logic Vulnerability\n* Improper Input Validation (CWE-20)\n\n---\n\n## Description\n\nWhile reviewing the discount functionality, it was discovered that the application accepts negative discount values through the administrative interface.\n\nExample values tested:\n\n```text\n-50.00\n-99,999,999.00\n```\n\nThe application accepted these values without validation and stored them in the database.\n\nExample records observed in the `sh_discounts` table:\n\n```text\n1 | QCZ5Y3HESM | fixed_amount | -5000\n4 | TOZKAHCB4S | fixed_amount | -9999999900\n```\n\nThis demonstrates that negative discount values are successfully persisted.\n\n---\n\n## Steps to Reproduce\n\n### 1. Create a Discount\n\nLogin as an administrator.\n\nNavigate to:\n\n```text\n/cpanel/discounts\n```\n\nCreate a new discount with the following values:\n\n```text\nType: fixed_amount\nValue: -99999999\n```\n\nSave the discount.\n\n### 2. Observe Successful Creation\n\nThe discount is accepted by the application and displayed in the administration interface.\n\nExample:\n\n```text\nCode: TOZKAHCB4S\nAmount: -$99,999,999.00\n```\n\n### 3. Verify Database Persistence\n\nInspect the database:\n\n```sql\nselect * from sh_discounts;\n```\n\nObserved entry:\n\n```text\nTOZKAHCB4S | fixed_amount | -9999999900\n```\n\n---\n\n## Technical Analysis\n\n### Discount Calculation\n\nFile:\n\n```text\nvendor/shopper/cart/src/Discounts/DiscountCalculator.php\n```\n\nObserved code:\n\n```php\n$fixedAmount = $discount-\u003evalue;\n```\n\nThe value is later processed without validation:\n\n```php\n$fixedAmount = min($fixedAmount, $applicableSubtotal);\n```\n\nWhen a negative value is supplied:\n\n```php\nmin(-9999999900, 10000)\n```\n\nreturns:\n\n```php\n-9999999900\n```\n\nallowing the negative value to continue through the calculation pipeline.\n\nThe resulting adjustment values are inserted into the database:\n\n```php\nCartLineAdjustment::query()-\u003einsert($adjustments);\n```\n\nNo validation was identified to ensure that discount amounts are positive before calculations occur.\n\n---\n\n### Final Total Calculation\n\nFile:\n\n```text\nvendor/shopper/cart/src/Pipelines/Calculate.php\n```\n\nObserved logic:\n\n```php\n$context-\u003etotal = max(\n    0,\n    $context-\u003etaxInclusive\n        ? $context-\u003esubtotal - $context-\u003ediscountTotal\n        : $context-\u003esubtotal - $context-\u003ediscountTotal + $context-\u003etaxTotal\n);\n```\n\nBecause negative discount values are allowed to reach this stage, financial calculations are performed using malformed discount data.\n\nExample:\n\n```text\nSubtotal      = 10000\nDiscountTotal = -5000\n```\n\nResulting calculation:\n\n```text\n10000 - (-5000)\n```\n\nResult:\n\n```text\n15000\n```\n\nThis demonstrates that negative discount values directly affect order total calculations.\n\n---\n\n## Impact\n\nThe following was confirmed:\n\n* Negative discount values are accepted.\n* Negative discount values are persisted.\n* Negative discount values are processed by the discount calculation engine.\n* Negative discount values affect order total calculations.\n\nPotential consequences include:\n\n* Incorrect pricing calculations.\n* Financial data integrity issues.\n* Unexpected order totals.\n* Violated assumptions within downstream pricing logic.\n* Future vulnerabilities if additional components assume discount values are always positive.\n\nBecause Shopper is a headless e-commerce administration framework and does not ship with a customer-facing storefront, it was not verified a customer-facing exploitation path.\n\nHowever, malformed discount records currently propagate through pricing calculations without validation.\n\n---\n\n## Recommendation\n\nImplement server-side validation enforcing positive discount values before persistence and before entering the calculation pipeline.\n\nSuggested validation:\n\n### Fixed Amount Discounts\n\n```text\nvalue \u003e 0\n```\n\n### Percentage Discounts\n\n```text\n0 \u003c value \u003c= 100\n```\n\nAdditionally, existing discount records should be validated before calculation to prevent malformed data from influencing pricing logic.\n\n---\n\n## Environment\n\n```text\nShopper Framework 2.8.1\nLaravel 12.61.1\nPHP 8.4.16\nSQLite\n```","status":"published","published":"2026-09-11T21:28:00Z","modified":"2026-09-11T21:45:09.894168904Z","cvss":[{"version":"3.1","score":6.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T15:46:38.911014Z"}]}}
