From 9194787e14157710912bea817481088e18cbd7da Mon Sep 17 00:00:00 2001 From: "venkatesh.r" Date: Mon, 9 Mar 2026 09:58:49 +0530 Subject: [PATCH 01/44] CHANGE_TPA_RECON --- app/Config/Routes.php | 2 + app/Controllers/EmployeeController.php | 122 +++++++++++++++++++++---- 2 files changed, 108 insertions(+), 16 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 2c4d629c..17126e97 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -227,6 +227,8 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) { $routes->get("retail-endorsement-list", "EmployeeController::retailendorsementlist"); $routes->post("retail-endorsement-save", "EmployeeController::retailendorsementsave"); $routes->get("getTPADataVariationReport/(:num)", "EmployeeController::getTPADataVariationReport/$1"); + $routes->get("getTPADataVariationReportView/(:num)", "EmployeeController::getTPADataVariationReport/$1/view"); + $routes->get("proceedTPADataVariationNextStep/(:num)", "EmployeeController::proceedTPADataVariationNextStep/$1"); $routes->get("bulkGenerateEcardAndStoreinS3", "EmployeeController::bulkGenerateEcardAndStoreinS3"); $routes->get('clearCdSession', 'EmployeeController::clearCdSession'); $routes->get('checkSessionStatus', 'EmployeeController::checkSessionStatus'); diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php index 01664e76..76e2b811 100755 --- a/app/Controllers/EmployeeController.php +++ b/app/Controllers/EmployeeController.php @@ -3938,14 +3938,14 @@ class EmployeeController extends AdminController } } - public function getTPADataVariationReport($file_id) + public function getTPADataVariationReport($file_id, $type = 'download') { $file_info = $this->batchFileModel->where('id', $file_id)->find(); $client_id = $file_info[0]['client_id']; $client_policy_id = $file_info[0]['client_policy_id']; $TpaApiDataModel = new TpaApiDataModel(); $emp_data_wo_tpa_id = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id); - + //loop emp data with TPA data for matches foreach ($emp_data_wo_tpa_id as $db_key => $db_row) @@ -3965,10 +3965,10 @@ class EmployeeController extends AdminController // die(); //not_in_tpa $tpa_emp_codes = $TpaApiDataModel->select('emp_code') - ->where('file_id', $file_id) - ->where('is_active', 1) - ->groupBy('emp_code') - ->findAll(); + ->where('file_id', $file_id) + ->where('is_active', 1) + ->groupBy('emp_code') + ->findAll(); $tpa_emp_codes = array_column($tpa_emp_codes, 'emp_code'); $not_in_tpa = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id,$tpa_emp_codes); @@ -3982,20 +3982,110 @@ class EmployeeController extends AdminController ->where('is_active',1) ->where('file_id',$file_id) ->whereNotIn('emp_code',$master_emp_codes) - ->findAll(); + ->findAll(); // d($not_in_nhance);die(); - if( !empty($not_in_tpa) || !empty($not_in_nhance) || !empty($emp_data_wo_tpa_id) ) - { - $this->exportVariationReportExcel($not_in_tpa,$not_in_nhance,$emp_data_wo_tpa_id); - } - else - { - return false; - } - // $this->exportVariationReportExcel([],[],[]); + if (!empty($not_in_tpa) || !empty($not_in_nhance) || !empty($emp_data_wo_tpa_id)) { + if ($type === 'download') { + $this->exportVariationReportExcel($not_in_tpa, $not_in_nhance, $emp_data_wo_tpa_id); + } else { + $response = [ + 'not_in_tpa' => $not_in_tpa, + 'not_in_nhance' => $not_in_nhance, + 'mismatch_data' => $emp_data_wo_tpa_id, + ]; + return $this->respond( + [ + 'status' => true, + 'code' => 200, + 'message' => '', + 'data' => $response, + ], + 200 + ); + } + } else { + if ($type === 'download') { + return false; + } + return $this->respond( + [ + 'status' => false, + 'code' => 202, + 'message' => 'No data found', + 'data' => [], + ], + 200 + ); + } + } + + public function proceedTPADataVariationNextStep($file_id) + { + try { + if (empty($file_id)) { + return $this->respond( + [ + 'status' => false, + 'code' => 400, + 'message' => 'Invalid file reference', + 'data' => [], + ], + 200 + ); + } + + $file = $this->batchFileModel->find($file_id); + + if (!$file) { + return $this->respond( + [ + 'status' => false, + 'code' => 404, + 'message' => 'File not found', + 'data' => [], + ], + 200 + ); + } + + $this->myLogger->logme( + 'error', + 'TPA variation review completed and proceed to next clicked', + [ + 'file_id' => $file_id, + 'user_id' => get_session_userid(), + ] + ); + + return $this->respond( + [ + 'status' => true, + 'code' => 200, + 'message' => 'Proceed to next step recorded successfully.', + 'data' => [], + ], + 200 + ); + } catch (\Throwable $e) { + $this->myLogger->logme( + 'error', + 'Error while processing TPA variation proceed to next: ' . $e->getMessage(), + ['file_id' => $file_id] + ); + + return $this->respond( + [ + 'status' => false, + 'code' => 500, + 'message' => 'Unable to proceed to the next step at the moment.', + 'data' => [], + ], + 200 + ); + } } From 157fa892adda201a2598b773996abd0ff4c4b631 Mon Sep 17 00:00:00 2001 From: "venkatesh.r" Date: Mon, 9 Mar 2026 10:17:39 +0530 Subject: [PATCH 02/44] CHANGE_ADD_JOB_FOR_VISIT_DELETION --- app/Controllers/EmpDataServiceController.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/Controllers/EmpDataServiceController.php b/app/Controllers/EmpDataServiceController.php index 3ea1af3a..45e7097c 100755 --- a/app/Controllers/EmpDataServiceController.php +++ b/app/Controllers/EmpDataServiceController.php @@ -3891,6 +3891,13 @@ class EmpDataServiceController extends BaseController $policy_name = $this->getPolicyNameUsingClientPolicyId($client_policy_id); $job_details = new Jobs(); + + $r = Jobs::addJob(['job_name' => 'visitOffBoard', 'payload' => [ + 'memberIds' => $employee_policy_table_primaryKey ?? [], + 'policyNumber' => $policy_name['policy_no'] ?? null , + 'source' => 'NHANCE' + ]]); + $r = Jobs::addJob(['job_name' => 'cashDepositCalculationForDeletion', 'payload' => [ 'employeeIds' => $employee_policy_table_primaryKey, 'client_id' => $client_id, @@ -4703,7 +4710,7 @@ class EmpDataServiceController extends BaseController public function getPolicyNameUsingClientPolicyId($client_policy_id) { - return $this->clientPolicyModel->select('policy_type.policy_type as policy_name') + return $this->clientPolicyModel->select('policy_type.policy_type as policy_name, client_policy.policy_no') ->join('policy_type', 'policy_type.id = client_policy.policy_type_id') ->where('client_policy.id', $client_policy_id) ->first(); From 6b99b9961b6e4e3b270a4c996ae2995697711dfd Mon Sep 17 00:00:00 2001 From: "venkatesh.r" Date: Tue, 10 Mar 2026 10:22:07 +0530 Subject: [PATCH 03/44] CHANGE_HR_ACCESS_CONTROLL_INSIGHT_ADD --- app/Views/hr_access_controll.php | 75 +++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/app/Views/hr_access_controll.php b/app/Views/hr_access_controll.php index f2d69989..6e1dd2ac 100644 --- a/app/Views/hr_access_controll.php +++ b/app/Views/hr_access_controll.php @@ -1,4 +1,58 @@
@@ -72,6 +132,7 @@ by + @@ -157,36 +218,39 @@ - +
+ + - @@ -228,10 +292,252 @@ + + \ No newline at end of file From fc7941ed26515ea66456dcc8a56c9642bbae3764 Mon Sep 17 00:00:00 2001 From: "sanjeev.p" Date: Thu, 12 Mar 2026 15:25:56 +0530 Subject: [PATCH 10/44] FIX_FAQ_and_FE_Content --- .../AppContentManagementController.php | 242 ++++++++++++++++-- app/Views/client_onboarding.php | 2 +- app/Views/faq_list.php | 14 +- app/Views/frontend_content_list.php | 27 +- .../policy_transaction_inception_form.php | 20 +- .../policy_transaction_inception_list.php | 6 +- .../assets/images/sales_tracker_light_sb.png | Bin 0 -> 1815 bytes 7 files changed, 265 insertions(+), 46 deletions(-) create mode 100644 public/assets/images/sales_tracker_light_sb.png diff --git a/app/Controllers/AppContentManagementController.php b/app/Controllers/AppContentManagementController.php index c3bcde67..e13732b2 100755 --- a/app/Controllers/AppContentManagementController.php +++ b/app/Controllers/AppContentManagementController.php @@ -226,6 +226,14 @@ class AppContentManagementController extends AdminController if ($this->request->getMethod() === 'post') { + /** + * -------------------------------------------------------------------------- + * STEP 1: INITIAL VALIDATION + * -------------------------------------------------------------------------- + * These are the basic validation rules. For 'content' and 'notes', we only + * check if they are provided and within the allowed length. + * The more advanced security check for script tags happens next. + */ $rules = [ 'fe_id' => [ 'rules' => 'permit_empty|integer|is_natural', @@ -258,22 +266,20 @@ class AppContentManagementController extends AdminController 'regex_match' => 'Heading contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed' ] ], - 'content' => [ - 'rules' => 'required|max_length[5000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]', + 'content' => [ + 'rules' => 'required|max_length[5000]', 'errors' => [ - 'required' => 'Content is required', - 'max_length' => 'Content cannot exceed 5000 characters', - 'regex_match' => 'Content contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed' + 'required' => 'Content is required', + 'max_length' => 'Content cannot exceed 5000 characters', ] ], 'notes' => [ - 'rules' => 'required|max_length[1500]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]', + 'rules' => 'required|max_length[1500]', 'errors' => [ - 'required' => 'Notes are required', - 'max_length' => 'Notes cannot exceed 1500 characters', - 'regex_match' => 'Notes contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed' + 'required' => 'Notes are required', + 'max_length' => 'Notes cannot exceed 1500 characters', ] - ] + ], ]; if (!$this->validate($rules)) { @@ -284,9 +290,60 @@ class AppContentManagementController extends AdminController 'errors' => $this->validator->getErrors() ]); } - $request_post_data = $this->request->getPost(); - $data = sanitizeInputArrayAdvanced($request_post_data); - $id = $data['fe_id'] ?? null; + + /************************************************************************** + * REFACTORED SANITIZATION LOGIC (XSS Protection) + ************************************************************************** + * + * Per the user's request, we are avoiding the generic `sanitizeInputArrayAdvanced` + * on the `content` and `notes` fields, as they require special HTML + * handling. + * + * The new process is: + * 1. Get the raw `content` and `notes` directly from the POST request. + * 2. Perform the critical XSS validation on this raw content using `hasXssTags()`. + * If it fails, the request is rejected immediately. This satisfies all + * the failure test cases (Tests 4-9). + * 3. Take all *other* POST data and sanitize it using the generic + * `sanitizeInputArrayAdvanced` function. + * 4. Sanitize the now-validated `content` and `notes` using our specific + * `sanitizeHtml()` function, which allows safe HTML. + * 5. Combine the sanitized data into a final array for database insertion. + * + *************************************************************************/ + + // Step 1: Get raw `content` and `notes`. + $rawContent = $this->request->getPost('content'); + $rawNotes = $this->request->getPost('notes'); + + // Step 2: Perform critical XSS validation on raw input. + $xssErrors = []; + if ($this->hasXssTags($rawContent)) { + $xssErrors['content'] = 'Content contains restricted tags. Script, iframe and event handlers are not allowed'; + } + if ($this->hasXssTags($rawNotes)) { + $xssErrors['notes'] = 'Notes contains restricted tags. Script, iframe and event handlers are not allowed'; + } + + if (!empty($xssErrors)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $xssErrors + ]); + } + + // Step 3: Sanitize all *other* POST data. + $otherPostData = $this->request->getPost(); + unset($otherPostData['content'], $otherPostData['notes']); + $data = sanitizeInputArrayAdvanced($otherPostData); + + // Step 4 & 5: Sanitize and re-combine `content` and `notes`. + $data['content'] = $this->sanitizeHtml($rawContent); + $data['notes'] = $this->sanitizeHtml($rawNotes); + + $id = $data['fe_id'] ?? null; if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) { return $this->response->setStatusCode(400)->setJSON([ @@ -416,19 +473,17 @@ class AppContentManagementController extends AdminController ] ], 'question' => [ - 'rules' => 'required|max_length[1000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]', + 'rules' => 'required|max_length[1000]', 'errors' => [ 'required' => 'Question is required', 'max_length' => 'Question cannot exceed 1000 characters', - 'regex_match' => 'Question contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed' ] ], 'answer' => [ - 'rules' => 'required|max_length[5000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]', + 'rules' => 'required|max_length[5000]', 'errors' => [ 'required' => 'Answer is required', 'max_length' => 'Answer cannot exceed 5000 characters', - 'regex_match' => 'Answer contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed' ] ] ]; @@ -443,9 +498,50 @@ class AppContentManagementController extends AdminController ]); } - $request_post_data = $this->request->getPost(); - $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data); - $data = array_filter($sanitized_post_data, fn($v) => $v !== '' && $v !== null); + /************************************************************************** + * XSS PROTECTION FOR 'question' and 'answer' + ************************************************************************** + * + * Applying the same security model as `frontend_content`. + * + * 1. Validate raw `question` and `answer` for malicious tags using `hasXssTags()`. + * If found, reject the request immediately. + * 2. Sanitize all *other* fields using the generic `sanitizeInputArrayAdvanced`. + * 3. Sanitize the `question` and `answer` using the HTML-aware `sanitizeHtml()` + * function to allow safe tags before saving. + * + *************************************************************************/ + + // Step 1: Validate raw input for XSS threats. + $rawQuestion = $this->request->getPost('question'); + $rawAnswer = $this->request->getPost('answer'); + $xssErrors = []; + + if ($this->hasXssTags($rawQuestion)) { + $xssErrors['question'] = 'Question contains restricted tags. Script, iframe and event handlers are not allowed'; + } + if ($this->hasXssTags($rawAnswer)) { + $xssErrors['answer'] = 'Answer contains restricted tags. Script, iframe and event handlers are not allowed'; + } + + if (!empty($xssErrors)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => 'error', + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $xssErrors, + 'ref' => $ref + ]); + } + + // Step 2 & 3: Sanitize and combine data. + $otherPostData = $this->request->getPost(); + unset($otherPostData['question'], $otherPostData['answer']); + $data = sanitizeInputArrayAdvanced($otherPostData); + + $data['question'] = $this->sanitizeHtml($rawQuestion); + $data['answer'] = $this->sanitizeHtml($rawAnswer); + $id = $data['faq_id'] ?? null; if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) { @@ -470,12 +566,6 @@ class AppContentManagementController extends AdminController $msg = "Updated"; } - // if ($returnType === 'web') { - // return redirect()->back()->with($status ? 'success' : 'error', "FAQ $msg " . ($status ? 'successfully' : 'failed')); - // } - - // return $this->response->setJSON([ - // ])->setStatusCode($result ? 200 : 400); return $this->response->setJSON([ 'status' => $status ? 'success' : 'error', 'message' => "FAQ $msg " . ($status ? 'successfully' : 'failed'), @@ -611,4 +701,104 @@ class AppContentManagementController extends AdminController // } + + // Add these two private methods inside AppContentManagementController + + + /** + * ================================================================================= + * HTML SANITIZATION & VALIDATION HELPER METHODS + * ================================================================================= + * The following two methods are the core of the XSS protection logic. + */ + + + /** + * sanitizeHtml() + * + * This function cleans a string of HTML, ensuring it is safe to display in a browser. + * It allows a specific set of safe HTML tags and removes any dangerous attributes + * from those tags. + * + * @param string $input The raw HTML string from user input. + * @return string The cleaned, safe HTML string. + */ + private function sanitizeHtml(string $input): string + { + /** + * Define a whitelist of allowed HTML tags. Any tag not in this list will be + * completely removed. We are allowing basic formatting, lists, tables, etc. + */ + // ✅ Added , ,

-

,
,
, , 
for Jodit support + $allowed_tags = '