Files
opensourcepos/tests/Models/Reports/Summary_discounts_test.php
jekkos 418580a52d Fix second-order SQL injection in currency_symbol config (#4390)
* Fix second-order SQL injection in currency_symbol config

The currency_symbol value was concatenated directly into SQL queries
without proper escaping, allowing SQL injection attacks via the
Summary Discounts report.

Changes:
- Use $this->db->escape() in Summary_discounts::getData() to properly
  escape the currency symbol value before concatenation
- Add htmlspecialchars() validation in Config::postSaveLocale() to
  sanitize the input at storage time
- Add unit tests to verify escaping of malicious inputs

Fixes SQL injection vulnerability described in bug report where
attackers with config permissions could inject arbitrary SQL through
the currency_symbol field.

* Update test to use CIUnitTestCase for consistency

Per code review feedback, updated test to extend CIUnitTestCase
instead of PHPUnit TestCase to maintain consistency with other
tests in the codebase.

---------

Co-authored-by: Ollama <ollama@steganos.dev>
2026-03-06 17:01:38 +01:00

51 lines
1.6 KiB
PHP

<?php
namespace Tests\Models\Reports;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\Reports\Summary_discounts;
class Summary_discounts_test extends CIUnitTestCase
{
public function testCurrencySymbolEscaping(): void
{
$malicious_symbols = [
'"',
"'",
'" + SLEEP(5) + "',
'", SLEEP(5), "',
"' + (SELECT * FROM (SELECT(SLEEP(5)))a) + '",
'"; DROP TABLE ospos_sales_items; --',
'" OR 1=1 --'
];
foreach ($malicious_symbols as $symbol) {
$escaped = $this->escapeCurrencySymbol($symbol);
$this->assertStringNotContainsString('SLEEP', $escaped, "SQL injection attempt should be escaped: $symbol");
$this->assertStringNotContainsString('DROP', $escaped, "SQL injection attempt should be escaped: $symbol");
$this->assertStringNotContainsString(';', $escaped, "Query termination should be escaped: $symbol");
}
}
public function testNormalCurrencySymbolHandling(): void
{
$normal_symbols = ['$', '€', '£', '¥', '₹', '₩', '₽', 'kr', 'CHF'];
foreach ($normal_symbols as $symbol) {
$escaped = $this->escapeCurrencySymbol($symbol);
$this->assertNotEmpty($escaped, "Normal currency symbol should be preserved: $symbol");
}
}
private function escapeCurrencySymbol(string $symbol): string
{
if (strlen($symbol) === 0) {
return "''";
}
$symbol = addslashes($symbol);
return "'" . $symbol . "'";
}
}