Fix security gaps and dead code found in codebase review

- mailer.php: re-enable TLS certificate verification for CyberMail API
  calls (was CURLOPT_SSL_VERIFYPEER => false, exposing outbound mail
  traffic to MITM).
- .htaccess / api/.htaccess: block direct HTTP access to .git/, db/,
  and api/config.php via mod_rewrite [F] rules. Confirmed live that
  /.git/config, /.git/logs/HEAD, and /db/schema.sql were all directly
  downloadable (200 OK with real content) - .git exposure leaks the
  repo's embedded GitHub token and full history; schema.sql leaks the
  DB layout. Also drops the dead "/setup -> setup_password.php" rewrite,
  since that file was already deleted from production.
- index.php: remove the 'download' route, which required a
  api/download.php that has never existed in this repo - confirmed
  live that GET /api/download returns a fatal 500.
- functions.php / upload.php / testimonials.php: extract the
  duplicated image-upload validation/move logic (MIME check, size
  check, UUID rename, move_uploaded_file) into one handleImageUpload()
  helper used by both upload endpoints.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 14:21:19 -05:00
parent ee699fccf7
commit 0dfcfa2f7e
7 changed files with 61 additions and 58 deletions
+37
View File
@@ -85,3 +85,40 @@ function validateRequired($data, $requiredFields) {
return $errors;
}
/**
* Validate an uploaded image file ($_FILES['file']) and move it into
* UPLOAD_DIR under a fresh UUID filename. Shared by all endpoints that
* accept image uploads (was previously duplicated per-endpoint).
*
* Returns ['url' => ..., 'filename' => ...] on success, or
* ['error' => ..., 'status' => ...] on failure.
*/
function handleImageUpload($file) {
if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
return ['error' => 'File upload failed', 'status' => 400];
}
if ($file['size'] > MAX_UPLOAD_SIZE) {
return ['error' => 'File too large. Maximum size is 5MB', 'status' => 400];
}
$allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mimeType, $allowedTypes)) {
return ['error' => 'Invalid file type. Only JPG, PNG, and WebP allowed', 'status' => 400];
}
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$filename = generateUuid() . '.' . $extension;
$filepath = UPLOAD_DIR . $filename;
if (!move_uploaded_file($file['tmp_name'], $filepath)) {
return ['error' => 'Failed to save file', 'status' => 500];
}
return ['url' => '/api/uploads/' . $filename, 'filename' => $filename];
}