mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-10-20 07:14:33 -04:00
Use Content-Type application/json for AJAX responses (#4357)
Complete Content-Type application/json fix for all AJAX responses - Add missing return statements to all ->response->setJSON() calls - Fix Items.php method calls from JSON() to setJSON() - Convert echo statements to proper JSON responses - Ensure consistent Content-Type headers across all controllers - Fix 46+ instances across 12 controller files - Change Config.php methods to : ResponseInterface (all return setJSON only): - postSaveRewards(), postSaveBarcode(), postSaveReceipt() - postSaveInvoice(), postRemoveLogo() - Update PHPDoc @return tags - Change Receivings.php _reload() to : string (only returns view) - Change Receivings.php methods to : string (all return _reload()): - getIndex(), postSelectSupplier(), postChangeMode(), postAdd() - postEditItem(), getDeleteItem(), getRemoveSupplier() - postComplete(), postRequisitionComplete(), getReceipt(), postCancelReceiving() - Change postSave() to : ResponseInterface (returns setJSON) - Update all PHPDoc @return tags Fix XSS vulnerabilities in sales templates, login, and config pages This commit addresses 5 XSS vulnerabilities by adding proper escaping to all user-controlled configuration values in HTML contexts. Fixed Files: - app/Views/sales/invoice.php: Escaped company_logo (URL context) and company (HTML) - app/Views/sales/work_order.php: Escaped company_logo (URL context) - app/Views/sales/receipt_email.php: Added file path validation and escaping for logo - app/Views/login.php: Escaped all config values in title, logo src, and alt - app/Views/configs/info_config.php: Escaped company_logo (URL context) Security Impact: - Prevents stored XSS attacks if configuration is compromised - Defense-in-depth principle applied to administrative interfaces - Follows OWASP best practices for output encoding Testing: - Verified no script execution with XSS payloads in config values - Confirmed proper escaping in HTML, URL, and file contexts - All templates render correctly with valid configuration Severity: High (4 files), Medium-High (1 file) CVSS Score: ~6.1 CWE: CWE-79 (Improper Neutralization of Input During Web Page Generation) Fix critical password validation bypass and add unit tests This commit addresses a critical security vulnerability where the password minimum length check was performed on the HASHED password (always 60 characters for bcrypt) instead of the actual password before hashing. Vulnerability Details: - Original code: strlen($employee_data['password']) >= 8 - This compared the hash length (always 60) instead of raw password - Impact: Users could set 1-character passwords like "a" - Severity: Critical (enables brute force attacks on weak passwords) - CVE-like issue: CWE-307 (Improper Restriction of Excessive Authentication Attempts) Fix Applied: - Validate password length BEFORE hashing - Clear error message when password is too short - Added unit tests to verify minimum length enforcement - Regression test to prevent future vulnerability re-introduction Test Coverage: - testPasswordMinLength_Rejects7Characters: Verify 7 chars rejected - testPasswordMinLength_Accepts8Characters: Verify 8 chars accepted - testPasswordMinLength_RejectsEmptyString: Verify empty rejected - testPasswordMinLength_RejectsWhitespaceOnly: Verify whitespace rejected - testPasswordMinLength_AcceptsSpecialCharacters: Verify special chars OK - testPasswordMinLength_RejectsPreviousBehavior: Regression test for bug Files Modified: - app/Controllers/Home.php: Fixed password validation logic - tests/Controllers/HomeTest.php: Added comprehensive unit tests Security Impact: - Enforces 8-character minimum password policy - Prevents extremely weak passwords that facilitate brute-force attacks - Critical for credential security and user account protection Breaking Changes: - Users with passwords < 8 characters will need to reset their password - This is the intended security improvement Severity: Critical CVSS Score: ~7.5 CWE: CWE-305 (Authentication Bypass by Primary Weakness), CWE-307 Add GitHub Actions workflow to run PHPUnit tests Move business logic from views to controllers for better separation of concerns - Move logo URL computation from info_config view to Config::getIndex() - Move image base64 encoding from receipt_email view to Sales controller - Improves separation of concerns by keeping business logic in controllers - Simplifies view templates to only handle presentation Fix XSS vulnerabilities in report views - escape user-controllable summary data and labels Fix base64 encoding URL issue in delete payment - properly URL encode base64 string Fix remaining return type declarations for Sales controller Fixed additional methods that call _reload(): - postAdd() - returns _reload($data) - postAddPayment() - returns _reload($data) - postEditItem() - returns _reload($data) - postSuspend() - returns _reload($data) - postSetPaymentType() - returns _reload() All methods now return ResponseInterface|string to match _reload() signature. This resolves PHP TypeError errors.
This commit is contained in:
1 parent
0858a1c23c
commit
690f43578d
44 files changed
+1354
-884
No files matched your search
+68
-64
@@ -17,6 +17,7 @@ use App\Models\Stock_location;
|
||||
use App\Models\Tax;
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use CodeIgniter\Encryption\EncrypterInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Config\Database;
|
||||
use Config\OSPOS;
|
||||
use Config\Services;
|
||||
@@ -215,8 +216,9 @@ class Config extends Secure_Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIndex(): void
|
||||
public function getIndex(): string
|
||||
{
|
||||
$data['stock_locations'] = $this->stock_location->get_all()->getResultArray();
|
||||
$data['dinner_tables'] = $this->dinner_table->get_all()->getResultArray();
|
||||
@@ -224,6 +226,7 @@ class Config extends Secure_Controller
|
||||
$data['support_barcode'] = $this->barcode_lib->get_list_barcodes();
|
||||
$data['barcode_fonts'] = $this->barcode_lib->listfonts('fonts');
|
||||
$data['logo_exists'] = $this->config['company_logo'] != '';
|
||||
$data['logo_src'] = !empty($this->config['company_logo']) ? base_url('uploads/' . $this->config['company_logo']) : '';
|
||||
$data['line_sequence_options'] = $this->sale_lib->get_line_sequence_options();
|
||||
$data['register_mode_options'] = $this->sale_lib->get_register_mode_options();
|
||||
$data['invoice_type_options'] = $this->sale_lib->get_invoice_type_options();
|
||||
@@ -272,17 +275,17 @@ class Config extends Secure_Controller
|
||||
|
||||
$data['mailchimp']['lists'] = $this->_mailchimp();
|
||||
|
||||
echo view('configs/manage', $data);
|
||||
return view('configs/manage', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves company information. Used in app/Views/configs/info_config.php
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveInfo(): void
|
||||
public function postSaveInfo(): ResponseInterface
|
||||
{
|
||||
$upload_data = $this->upload_logo();
|
||||
$upload_success = empty($upload_data['error']);
|
||||
@@ -306,7 +309,7 @@ class Config extends Secure_Controller
|
||||
$message = lang('Config.saved_' . ($success ? '' : 'un') . 'successfully');
|
||||
$message = $upload_success ? $message : strip_tags($upload_data['error']);
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => $message]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => $message]);
|
||||
}
|
||||
|
||||
|
||||
@@ -358,9 +361,10 @@ class Config extends Secure_Controller
|
||||
* Saves general configuration. Used in app/Views/configs/general_config.php
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveGeneral(): void
|
||||
public function postSaveGeneral(): ResponseInterface
|
||||
{
|
||||
$batch_save_data = [
|
||||
'theme' => $this->request->getPost('theme'),
|
||||
@@ -407,16 +411,16 @@ class Config extends Secure_Controller
|
||||
|
||||
$success = $this->appconfig->batch_save($batch_save_data);
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a number against the currently selected locale. Used in app/Views/configs/locale_config.php
|
||||
*
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postCheckNumberLocale(): void
|
||||
public function postCheckNumberLocale(): ResponseInterface
|
||||
{
|
||||
$number_locale = $this->request->getPost('number_locale');
|
||||
$save_number_locale = $this->request->getPost('save_number_locale');
|
||||
@@ -438,7 +442,7 @@ class Config extends Secure_Controller
|
||||
$fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $currency_symbol);
|
||||
$number_local_example = $fmt->format(1234567890.12300);
|
||||
|
||||
echo json_encode([
|
||||
return $this->response->setJSON([
|
||||
'success' => $number_local_example != false,
|
||||
'save_number_locale' => $save_number_locale,
|
||||
'number_locale_example' => $number_local_example,
|
||||
@@ -451,10 +455,10 @@ class Config extends Secure_Controller
|
||||
* Saves locale configuration. Used in app/Views/configs/locale_config.php
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveLocale(): void
|
||||
public function postSaveLocale(): ResponseInterface
|
||||
{
|
||||
$exploded = explode(":", $this->request->getPost('language'));
|
||||
$batch_save_data = [
|
||||
@@ -480,17 +484,17 @@ class Config extends Secure_Controller
|
||||
|
||||
$success = $this->appconfig->batch_save($batch_save_data);
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves email configuration. Used in app/Views/configs/email_config.php
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveEmail(): void
|
||||
public function postSaveEmail(): ResponseInterface
|
||||
{
|
||||
$password = '';
|
||||
|
||||
@@ -511,17 +515,17 @@ class Config extends Secure_Controller
|
||||
|
||||
$success = $this->appconfig->batch_save($batch_save_data);
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves SMS message configuration. Used in app/Views/configs/message_config.php.
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveMessage(): void
|
||||
public function postSaveMessage(): ResponseInterface
|
||||
{
|
||||
$password = '';
|
||||
|
||||
@@ -538,7 +542,7 @@ class Config extends Secure_Controller
|
||||
|
||||
$success = $this->appconfig->batch_save($batch_save_data);
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -565,15 +569,15 @@ class Config extends Secure_Controller
|
||||
/**
|
||||
* Gets Mailchimp lists when a valid API key is inserted. Used in app/Views/configs/integrations_config.php
|
||||
*
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postCheckMailchimpApiKey(): void
|
||||
public function postCheckMailchimpApiKey(): ResponseInterface
|
||||
{
|
||||
$lists = $this->_mailchimp($this->request->getPost('mailchimp_api_key'));
|
||||
$success = count($lists) > 0;
|
||||
|
||||
echo json_encode([
|
||||
return $this->response->setJSON([
|
||||
'success' => $success,
|
||||
'message' => lang('Config.mailchimp_key_' . ($success ? '' : 'un') . 'successfully'),
|
||||
'mailchimp_lists' => $lists
|
||||
@@ -584,10 +588,10 @@ class Config extends Secure_Controller
|
||||
* Saves Mailchimp configuration. Used in app/Views/configs/integrations_config.php
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveMailchimp(): void
|
||||
public function postSaveMailchimp(): ResponseInterface
|
||||
{
|
||||
$api_key = '';
|
||||
$list_id = '';
|
||||
@@ -608,56 +612,56 @@ class Config extends Secure_Controller
|
||||
|
||||
$success = $this->appconfig->batch_save($batch_save_data);
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all stock locations. Used in app/Views/configs/stock_config.php
|
||||
*
|
||||
* @return void
|
||||
* @return string
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function getStockLocations(): void
|
||||
public function getStockLocations(): string
|
||||
{
|
||||
$stock_locations = $this->stock_location->get_all()->getResultArray();
|
||||
|
||||
echo view('partial/stock_locations', ['stock_locations' => $stock_locations]);
|
||||
return view('partial/stock_locations', ['stock_locations' => $stock_locations]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @return string
|
||||
*/
|
||||
public function getDinnerTables(): void
|
||||
public function getDinnerTables(): string
|
||||
{
|
||||
$dinner_tables = $this->dinner_table->get_all()->getResultArray();
|
||||
|
||||
echo view('partial/dinner_tables', ['dinner_tables' => $dinner_tables]);
|
||||
return view('partial/dinner_tables', ['dinner_tables' => $dinner_tables]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets all tax categories.
|
||||
*
|
||||
* @return void
|
||||
* @return string
|
||||
*/
|
||||
public function ajax_tax_categories(): void // TODO: Is this function called anywhere in the code?
|
||||
public function ajax_tax_categories(): string // TODO: Is this function called anywhere in the code?
|
||||
{
|
||||
$tax_categories = $this->tax->get_all_tax_categories()->getResultArray();
|
||||
|
||||
echo view('partial/tax_categories', ['tax_categories' => $tax_categories]);
|
||||
return view('partial/tax_categories', ['tax_categories' => $tax_categories]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all customer rewards. Used in app/Views/configs/reward_config.php
|
||||
*
|
||||
* @return void
|
||||
* @return string
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function getCustomerRewards(): void
|
||||
public function getCustomerRewards(): string
|
||||
{
|
||||
$customer_rewards = $this->customer_rewards->get_all()->getResultArray();
|
||||
|
||||
echo view('partial/customer_rewards', ['customer_rewards' => $customer_rewards]);
|
||||
return view('partial/customer_rewards', ['customer_rewards' => $customer_rewards]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -677,10 +681,10 @@ class Config extends Secure_Controller
|
||||
/**
|
||||
* Saves stock locations. Used in app/Views/configs/stock_config.php
|
||||
*
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveLocations(): void
|
||||
public function postSaveLocations(): ResponseInterface
|
||||
{
|
||||
$this->db->transStart();
|
||||
|
||||
@@ -712,17 +716,17 @@ class Config extends Secure_Controller
|
||||
|
||||
$success = $this->db->transStatus();
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all dinner tables. Used in app/Views/configs/table_config.php
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveTables(): void
|
||||
public function postSaveTables(): ResponseInterface
|
||||
{
|
||||
$this->db->transStart();
|
||||
|
||||
@@ -759,17 +763,17 @@ class Config extends Secure_Controller
|
||||
|
||||
$success = $this->db->transStatus();
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves tax configuration. Used in app/Views/configs/tax_config.php
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveTax(): void
|
||||
public function postSaveTax(): ResponseInterface
|
||||
{
|
||||
$default_tax_1_rate = $this->request->getPost('default_tax_1_rate');
|
||||
$default_tax_2_rate = $this->request->getPost('default_tax_2_rate');
|
||||
@@ -791,17 +795,17 @@ class Config extends Secure_Controller
|
||||
|
||||
$message = lang('Config.saved_' . ($success ? '' : 'un') . 'successfully');
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => $message]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => $message]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves customer rewards configuration. Used in app/Views/configs/reward_config.php
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveRewards(): void
|
||||
* @throws ReflectionException
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveRewards(): ResponseInterface
|
||||
{
|
||||
$this->db->transStart();
|
||||
|
||||
@@ -845,17 +849,17 @@ class Config extends Secure_Controller
|
||||
|
||||
$success = $this->db->transStatus();
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves barcode configuration. Used in app/Views/configs/barcode_config.php
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveBarcode(): void
|
||||
public function postSaveBarcode(): ResponseInterface
|
||||
{
|
||||
$batch_save_data = [
|
||||
'barcode_type' => $this->request->getPost('barcode_type'),
|
||||
@@ -877,17 +881,17 @@ class Config extends Secure_Controller
|
||||
|
||||
$success = $this->appconfig->batch_save($batch_save_data);
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves receipt configuration. Used in app/Views/configs/receipt_config.php.
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveReceipt(): void
|
||||
public function postSaveReceipt(): ResponseInterface
|
||||
{
|
||||
$batch_save_data = [
|
||||
'receipt_template' => $this->request->getPost('receipt_template'),
|
||||
@@ -912,17 +916,17 @@ class Config extends Secure_Controller
|
||||
|
||||
$success = $this->appconfig->batch_save($batch_save_data);
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves invoice configuration. Used in app/Views/configs/invoice_config.php.
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postSaveInvoice(): void
|
||||
public function postSaveInvoice(): ResponseInterface
|
||||
{
|
||||
$batch_save_data = [
|
||||
'invoice_enable' => $this->request->getPost('invoice_enable') != null,
|
||||
@@ -953,20 +957,20 @@ class Config extends Secure_Controller
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
return $this->response->setJSON(['success' => $success, 'message' => lang('Config.saved_' . ($success ? '' : 'un') . 'successfully')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the company logo from the database. Used in app/Views/configs/info_config.php.
|
||||
*
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @throws ReflectionException
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
public function postRemoveLogo(): void
|
||||
public function postRemoveLogo(): ResponseInterface
|
||||
{
|
||||
$success = $this->appconfig->save(['company_logo' => '']);
|
||||
|
||||
echo json_encode(['success' => $success]);
|
||||
return $this->response->setJSON(['success' => $success]);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user