From 0037133f8a4dc2f78e6edf802cd1ff8c4b6e3887 Mon Sep 17 00:00:00 2001 From: velz Date: Fri, 27 Feb 2026 13:03:30 +0530 Subject: [PATCH] FIX_BE_VALIDATION_FAQ&APP_CONTENT_MGMT --- .../AppContentManagementController.php | 284 +++++++++++++----- 1 file changed, 209 insertions(+), 75 deletions(-) diff --git a/app/Controllers/AppContentManagementController.php b/app/Controllers/AppContentManagementController.php index a53d90d0..c3bcde67 100755 --- a/app/Controllers/AppContentManagementController.php +++ b/app/Controllers/AppContentManagementController.php @@ -62,32 +62,40 @@ class AppContentManagementController extends AdminController $data = $this->request->getPost(); $sanitized_post_data = sanitizeInputArrayAdvanced($data); - $id = ((int) $sanitized_post_data['add_image_id']) ?? null; + $id = (int)($sanitized_post_data['add_image_id'] ?? 0); $rules = [ - 'client_id' => [ - 'rules' => 'required|integer', + 'add_image_id' => [ + 'rules' => 'permit_empty|integer|is_natural', 'errors' => [ - 'required' => 'Client is required', - 'integer' => 'Invalid client selected' + 'integer' => 'Image ID must be a valid number', + 'is_natural' => 'Image ID must be a non-negative number' + ] + ], + 'client_id' => [ + 'rules' => 'required|integer|is_natural_no_zero', + 'errors' => [ + 'required' => 'Client is required', + 'integer' => 'Invalid client selected', + 'is_natural_no_zero' => 'Invalid client selected' + ] + ], + 'advertise_image' => [ + 'rules' => ($id === 0 ? 'uploaded[advertise_image]|' : '') + . 'is_image[advertise_image]' + . '|mime_in[advertise_image,image/jpg,image/jpeg,image/png]' + . '|max_size[advertise_image,200]' + . '|min_dims[advertise_image,1640,664]' + . '|max_dims[advertise_image,1640,664]', + 'errors' => [ + 'uploaded' => 'Image is required', + 'is_image' => 'File must be an image', + 'mime_in' => 'Only JPG, JPEG, PNG allowed', + 'max_size' => 'Image size must not exceed 200 KB', + 'min_dims' => 'Image dimensions must be exactly 1640x664 pixels', + 'max_dims' => 'Image dimensions must be exactly 1640x664 pixels', ] ], - ]; - $rules['advertise_image'] = [ - 'rules' => ($id === 0 ? 'uploaded[advertise_image]|' : '') // required only for ADD - . 'is_image[advertise_image]' - . '|mime_in[advertise_image,image/jpg,image/jpeg,image/png]' - . '|max_size[advertise_image,200]' - . '|min_dims[advertise_image,1640,664]' - . '|max_dims[advertise_image,1640,664]', - 'errors' => [ - 'uploaded' => 'Image is required', - 'is_image' => 'File must be an image', - 'mime_in' => 'Only JPG, JPEG, PNG allowed', - 'max_size' => 'Image size must not exceed 200 KB', - 'min_dims' => 'Image dimensions must be exactly 1640x664 pixels', - 'max_dims' => 'Image dimensions must be exactly 1640x664 pixels', - ] ]; if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON([ @@ -100,7 +108,12 @@ class AppContentManagementController extends AdminController $file = $this->request->getFile('advertise_image'); - $client_id = $sanitized_post_data['client_id'] ?? null; + $client_id = (int)($sanitized_post_data['client_id'] ?? 0); + + $clientExists = $this->clientModel->where('id', $client_id)->where('is_active', 1)->first(); + if (!$clientExists) { + return $this->respond(['status' => false, 'message' => 'Invalid client selected.'], 400); + } if (!$file || !$file->isValid()) { return $this->respond(['status' => false, 'message' => 'No file uploaded or invalid file.'], 400); @@ -119,12 +132,15 @@ class AppContentManagementController extends AdminController $file->move($uploadPath, $fileName); - $id = $sanitized_post_data['add_image_id'] ?? null; $details = ['name' => $fileName,'client_id'=>$client_id]; - if ($id == 0) { + if ($id === 0) { $this->addImgModel->insert($details); } else { + $existingRecord = $this->addImgModel->find($id); + if (!$existingRecord) { + return $this->respond(['status' => false, 'message' => 'Record not found for update.'], 404); + } $this->addImgModel->update($id, $details); } @@ -138,14 +154,34 @@ class AppContentManagementController extends AdminController public function remove_advertise_image() { try { - $id = $this->request->getPost('add_image_id'); + $rules = [ + 'add_image_id' => [ + 'rules' => 'required|integer|is_natural_no_zero', + 'errors' => [ + 'required' => 'Image ID is required', + 'integer' => 'Image ID must be a valid number', + 'is_natural_no_zero' => 'Image ID must be a positive number' + ] + ] + ]; - if (!$id) { - return $this->respond(['status' => false, 'message' => 'ID missing'], 400); + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors() + ]); } - $data = ['is_active' => 0]; - $this->addImgModel->update($id, $data); + $id = (int)$this->request->getPost('add_image_id'); + + $existing = $this->addImgModel->find($id); + if (!$existing) { + return $this->respond(['status' => false, 'message' => 'Record not found'], 404); + } + + $this->addImgModel->update($id, ['is_active' => 0]); return $this->respond(['status' => true, 'message' => 'Deleted successfully']); @@ -158,19 +194,27 @@ class AppContentManagementController extends AdminController // Preview image Went Edit. public function showAdvertiseImage($filename) { - // $path = WRITEPATH . 'uploads/advertiseImage/' . $filename; - $path = ROOTPATH . 'public/uploads/add_image_upload/' . $filename; + $filename = basename($filename); - if (!file_exists($path)) { + if (!preg_match('/^[a-zA-Z0-9_\-]+\.(jpg|jpeg|png|gif|webp)$/i', $filename)) { throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); - // return $this->response->setStatusCode(404, 'File not found'); } - $mime = mime_content_type($path); - // header('Content-Type: ' . $mimeType); - // readfile($path); - // exit; - return $this->response->setHeader('Content-Type', $mime)->setBody(file_get_contents($path)); + $path = ROOTPATH . 'public/uploads/add_image_upload/' . $filename; + $realPath = realpath($path); + $allowedDir = realpath(ROOTPATH . 'public/uploads/add_image_upload'); + + if (!$realPath || strpos($realPath, $allowedDir) !== 0 || !file_exists($realPath)) { + throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + } + + $mime = mime_content_type($realPath); + $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; + if (!in_array($mime, $allowedMimes, true)) { + throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); + } + + return $this->response->setHeader('Content-Type', $mime)->setBody(file_get_contents($realPath)); } @@ -183,39 +227,51 @@ class AppContentManagementController extends AdminController if ($this->request->getMethod() === 'post') { $rules = [ - 'type' => [ - 'rules' => 'required|max_length[255]', + 'fe_id' => [ + 'rules' => 'permit_empty|integer|is_natural', 'errors' => [ - 'required' => 'Type is required', - 'max_length' => 'Type cannot exceed 255 characters' + 'integer' => 'ID must be a valid number', + 'is_natural' => 'ID must be a non-negative number' + ] + ], + 'type' => [ + 'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9_ \-]+$/]', + 'errors' => [ + 'required' => 'Type is required', + 'max_length' => 'Type cannot exceed 255 characters', + 'regex_match' => 'Type contains invalid characters' ] ], 'content_section' => [ - 'rules' => 'required|max_length[255]', + 'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9_ \-]+$/]', 'errors' => [ - 'required' => 'Content Section is required', - 'max_length' => 'Content Section cannot exceed 255 characters' + 'required' => 'Content Section is required', + 'max_length' => 'Content Section cannot exceed 255 characters', + 'regex_match' => 'Content Section contains invalid characters' ] ], 'heading' => [ - 'rules' => 'required|max_length[255]', + 'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/]+$/]', 'errors' => [ - 'required' => 'Heading is required', - 'max_length' => 'Heading cannot exceed 255 characters' + 'required' => 'Heading is required', + 'max_length' => 'Heading cannot exceed 255 characters', + 'regex_match' => 'Heading contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed' ] ], 'content' => [ - 'rules' => 'required|max_length[5000]', + 'rules' => 'required|max_length[5000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]', 'errors' => [ - 'required' => 'Content is required', - 'max_length' => 'Content cannot exceed 5000 characters' + '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' ] ], 'notes' => [ - 'rules' => 'required|max_length[1500]', + 'rules' => 'required|max_length[1500]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]', 'errors' => [ - 'required' => 'Notes are required', - 'max_length' => 'Notes cannot exceed 1500 characters' + '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' ] ] ]; @@ -230,8 +286,14 @@ class AppContentManagementController extends AdminController } $request_post_data = $this->request->getPost(); $data = sanitizeInputArrayAdvanced($request_post_data); - $id = $data['fe_id']; - + $id = $data['fe_id'] ?? null; + + if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, 'message' => 'Invalid ID format', 'code' => 400 + ]); + } + $id = !empty($id) ? (int)$id : null; unset($data['fe_id']); @@ -244,6 +306,12 @@ class AppContentManagementController extends AdminController $status = $this->feContentModel->insert($data); $text = "Created"; } else { + $existing = $this->feContentModel->find($id); + if (!$existing) { + return $this->response->setStatusCode(404)->setJSON([ + 'status' => false, 'message' => 'Record not found', 'code' => 404 + ]); + } $status = $this->feContentModel->update($id, $data); $text = "Updated"; } @@ -260,6 +328,13 @@ class AppContentManagementController extends AdminController $id = $this->request->getGet('fe_id') ?? null; if (!empty($id)) { + + if (!ctype_digit((string)$id) || (int)$id <= 0) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, 'code' => 400, 'message' => 'Invalid ID format' + ]); + } + $id = (int)$id; $data['fe_list'] = $this->feContentModel->where('id', $id)->orderBy('id', 'DESC')->findAll(); @@ -276,16 +351,24 @@ class AppContentManagementController extends AdminController } elseif ($method === 'delete') { - // $input = $this->request->getRawInput(); - $id = $this->request->getGet('fe_id'); // ✅ THIS - $id = $id ?? null; + $id = $this->request->getGet('fe_id') ?? null; - if (empty($id)) { + if (empty($id) || !ctype_digit((string)$id) || (int)$id <= 0) { + return $this->respond([ + 'status' => false, + 'code' => 400, + 'message' => 'Valid numeric ID is required for deletion' + ], 400); + } + $id = (int)$id; + + $existing = $this->feContentModel->find($id); + if (!$existing) { return $this->respond([ 'status' => false, 'code' => 404, - 'message' => 'No ID provided for deletion' - ], 200); + 'message' => 'Record not found' + ], 404); } $update_status = $this->feContentModel->where('id', $id)->set(['is_active' => 0])->update(); @@ -309,10 +392,15 @@ class AppContentManagementController extends AdminController } + private const ALLOWED_RETURN_TYPES = ['api', 'web']; + public function FAQ() { $method = strtolower($this->request->getMethod()); $returnType = strtolower($this->request->getGet('return_type') ?? 'api'); + if (!in_array($returnType, self::ALLOWED_RETURN_TYPES, true)) { + $returnType = 'api'; + } $ref = ['timestamp' => date('Y-m-d H:i:s')]; try { @@ -320,33 +408,64 @@ class AppContentManagementController extends AdminController if ($method === 'post') { $rules = [ 'category' => [ - 'rules' => 'required', + 'rules' => 'required|max_length[100]|alpha_numeric_space', 'errors' => [ - 'required' => 'Category is required' + 'required' => 'Category is required', + 'max_length' => 'Category cannot exceed 100 characters', + 'alpha_numeric_space' => 'Category contains invalid characters' ] ], 'question' => [ - 'rules' => 'required', + 'rules' => 'required|max_length[1000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]', 'errors' => [ - 'required' => 'Question is required' + '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', + 'rules' => 'required|max_length[5000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]', 'errors' => [ - 'required' => 'Answer is required' + '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' ] ] ]; + + if (!$this->validate($rules)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => 'error', + 'message' => 'Input validation failed', + 'code' => 400, + 'errors' => $this->validator->getErrors(), + 'ref' => $ref + ]); + } + $request_post_data = $this->request->getPost(); $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data); $data = array_filter($sanitized_post_data, fn($v) => $v !== '' && $v !== null); - $id = $data['faq_id']; + $id = $data['faq_id'] ?? null; + + if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => 'error', 'message' => 'Invalid FAQ ID format', 'code' => 400, 'ref' => $ref + ]); + } + $id = !empty($id) ? (int)$id : null; + unset($data['faq_id']); if (empty($id)) { $status = $this->faqModel->insert($data); $msg = "Created"; } else { + $existing = $this->faqModel->find($id); + if (!$existing) { + return $this->response->setStatusCode(404)->setJSON([ + 'status' => 'error', 'message' => 'FAQ not found', 'code' => 404, 'ref' => $ref + ]); + } $status = $this->faqModel->update($id, $data); $msg = "Updated"; } @@ -372,10 +491,17 @@ class AppContentManagementController extends AdminController $id = $this->request->getGet('faq_id'); if (!empty($id)) { + if (!ctype_digit((string)$id) || (int)$id <= 0) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => 'error', 'message' => 'Invalid FAQ ID format', 'code' => 400, 'ref' => $ref + ]); + } + $id = (int)$id; + if($returnType === 'web'){ - $row = $this->faqModel->find((int)$id); + $row = $this->faqModel->find($id); }else{ - $row = $this->faqModel->where('is_active', 1)->find((int)$id); + $row = $this->faqModel->where('is_active', 1)->find($id); } $data['faq_list'] = $row ? [$row] : []; @@ -417,7 +543,15 @@ class AppContentManagementController extends AdminController // --- 3. DELETE: SOFT DELETE --- elseif ($method === 'delete') { $id = $this->request->getGet('faq_id'); - $status = (!empty($id)) ? $this->faqModel->update($id, ['is_active' => 0]) : false; + + if (empty($id) || !ctype_digit((string)$id) || (int)$id <= 0) { + return $this->response->setJSON([ + 'status' => 'error', 'message' => 'Valid numeric FAQ ID is required', 'code' => 400, 'ref' => $ref + ])->setStatusCode(400); + } + $id = (int)$id; + + $status = $this->faqModel->find($id) ? $this->faqModel->update($id, ['is_active' => 0]) : false; return $this->response->setJSON([ 'status' => $status ? ($returnType === 'web' ? true : 'success') : ($returnType === 'web' ? false : 'error'), @@ -429,12 +563,12 @@ class AppContentManagementController extends AdminController } } catch (\Throwable $e) { - $msg = $e->getMessage(); + log_message('error', 'FAQ error: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine()); return $this->response->setJSON([ 'status' => $returnType === 'web' ? false : 'error', 'code' => 500, - 'message' => $msg, - 'ref' => ['file' => $e->getFile(), 'line' => $e->getLine()] + 'message' => 'An internal error occurred. Please try again later.', + 'ref' => $ref ])->setStatusCode(500); } }