| @@ -14,20 +14,38 @@ | ||
| 14 | 14 | |
| 15 | 15 | /** |
| 16 | 16 | * Handle Word document upload and processing |
| 17 | 17 | */ |
| 18 | -public function mxchat_handle_word_upload() { | |
| 18 | + public function mxchat_handle_word_upload() { | |
| 19 | 19 | check_ajax_referer('mxchat_chat_nonce', 'nonce'); |
| 20 | - | |
| 20 | + | |
| 21 | 21 | if (!isset($_FILES['word_file']) || !isset($_POST['session_id'])) { |
| 22 | 22 | wp_send_json_error(esc_html__('Missing required parameters.', 'mxchat')); |
| 23 | 23 | return; |
| 24 | 24 | } |
| 25 | - | |
| 25 | + | |
| 26 | + // SECURITY FIX: Check if Word uploads are enabled in settings | |
| 27 | + $options = get_option('mxchat_options', array()); | |
| 28 | + $show_word_button = isset($options['show_word_upload_button']) ? $options['show_word_upload_button'] : 'on'; | |
| 29 | + | |
| 30 | + if ($show_word_button !== 'on') { | |
| 31 | + wp_send_json_error(esc_html__('Word document uploads are currently disabled.', 'mxchat')); | |
| 32 | + return; | |
| 33 | + } | |
| 34 | + | |
| 26 | 35 | $file = $_FILES['word_file']; |
| 27 | 36 | $session_id = sanitize_text_field($_POST['session_id']); |
| 28 | 37 | $original_filename = sanitize_text_field($file['name']); |
| 29 | - | |
| 38 | + | |
| 39 | + // SECURITY FIX: Verify session ownership before allowing upload | |
| 40 | + $current_user_identifier = MxChat_User::mxchat_get_user_identifier(); | |
| 41 | + $session_owner = get_option("mxchat_session_owner_{$session_id}"); | |
| 42 | + | |
| 43 | + if ($session_owner && $session_owner !== $current_user_identifier) { | |
| 44 | + wp_send_json_error(esc_html__('Unauthorized access.', 'mxchat')); | |
| 45 | + return; | |
| 46 | + } | |
| 47 | + | |
| 30 | 48 | // Check file type |
| 31 | 49 | $allowed_types = array( |
| 32 | 50 | 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' |
| 33 | 51 | ); |
| @@ -36,23 +54,24 @@ | ||
| 36 | 54 | if (!$file_type['type']) { |
| 37 | 55 | wp_send_json_error(esc_html__('Invalid file type. Only .docx files are allowed.', 'mxchat')); |
| 38 | 56 | return; |
| 39 | 57 | } |
| 40 | - | |
| 41 | - // Generate unique filename | |
| 42 | - $word_filename = 'mxchat_word_' . $session_id . '_' . time() . '.docx'; | |
| 58 | + | |
| 59 | + // SECURITY FIX: Generate random filename without exposing session_id | |
| 60 | + $random_string = wp_generate_password(20, false, false); // 20 char alphanumeric string | |
| 61 | + $word_filename = 'mxchat_word_' . $random_string . '_' . time() . '.docx'; | |
| 43 | 62 | $word_path = $this->temp_dir . '/' . $word_filename; |
| 44 | - | |
| 63 | + | |
| 45 | 64 | if (!move_uploaded_file($file['tmp_name'], $word_path)) { |
| 46 | 65 | wp_send_json_error(esc_html__('Failed to upload file.', 'mxchat')); |
| 47 | 66 | return; |
| 48 | 67 | } |
| 49 | - | |
| 68 | + | |
| 50 | 69 | $this->mxchat_clear_word_transients($session_id); |
| 51 | - | |
| 70 | + | |
| 52 | 71 | // Process the document |
| 53 | 72 | $embeddings = $this->mxchat_process_word_document($word_path); |
| 54 | - | |
| 73 | + | |
| 55 | 74 | if ($embeddings === false || empty($embeddings)) { |
| 56 | 75 | unlink($word_path); |
| 57 | 76 | $error_message = $this->options['word_intent_error_text'] ?? |
| 58 | 77 | esc_html__('The uploaded document appears to be empty or contains unsupported content.', 'mxchat'); |
| @@ -58,18 +77,18 @@ | ||
| 58 | 77 | esc_html__('The uploaded document appears to be empty or contains unsupported content.', 'mxchat'); |
| 59 | 78 | wp_send_json_error($error_message); |
| 60 | 79 | return; |
| 61 | 80 | } |
| 62 | - | |
| 63 | - // Store the embeddings and file information | |
| 81 | + | |
| 82 | + // Store the mapping between session and the random filename | |
| 64 | 83 | set_transient('mxchat_word_url_' . $session_id, $word_path, HOUR_IN_SECONDS); |
| 65 | 84 | set_transient('mxchat_word_filename_' . $session_id, $original_filename, HOUR_IN_SECONDS); |
| 66 | 85 | set_transient('mxchat_word_embeddings_' . $session_id, $embeddings, HOUR_IN_SECONDS); |
| 67 | 86 | set_transient('mxchat_include_word_in_context_' . $session_id, true, HOUR_IN_SECONDS); |
| 68 | - | |
| 87 | + | |
| 69 | 88 | $success_message = $this->options['pdf_intent_success_text'] ?? |
| 70 | 89 | __("I've processed the document. What questions do you have about it?", 'mxchat'); |
| 71 | - | |
| 90 | + | |
| 72 | 91 | wp_send_json_success([ |
| 73 | 92 | 'message' => $success_message, |
| 74 | 93 | 'filename' => $original_filename |
| 75 | 94 | ]); |
| @@ -326,7 +345,34 @@ | ||
| 326 | 345 | } |
| 327 | 346 | |
| 328 | 347 | return $dotProduct / ($normA * $normB); |
| 329 | 348 | } |
| 349 | + | |
| 350 | + /** | |
| 351 | + * Check the status of a Word document for the current session | |
| 352 | + */ | |
| 353 | +public function mxchat_check_word_status() { | |
| 354 | + check_ajax_referer('mxchat_chat_nonce', 'nonce'); | |
| 355 | + | |
| 356 | + if (empty($_POST['session_id'])) { | |
| 357 | + wp_send_json_error(esc_html__('Session ID missing.', 'mxchat')); | |
| 358 | + return; | |
| 359 | + } | |
| 360 | + | |
| 361 | + $session_id = sanitize_text_field($_POST['session_id']); | |
| 362 | + $word_path = get_transient('mxchat_word_url_' . $session_id); | |
| 363 | + $filename = get_transient('mxchat_word_filename_' . $session_id); | |
| 364 | + | |
| 365 | + if ($word_path && file_exists($word_path) && $filename) { | |
| 366 | + wp_send_json_success([ | |
| 367 | + 'has_word' => true, | |
| 368 | + 'filename' => $filename | |
| 369 | + ]); | |
| 370 | + } else { | |
| 371 | + wp_send_json_success([ | |
| 372 | + 'has_word' => false | |
| 373 | + ]); | |
| 374 | + } | |
| 375 | +} | |
| 330 | 376 | |
| 331 | 377 | |
| 332 | 378 | } |