diff --git a/app/api/files.py b/app/api/files.py index 7da4f6ac..cad47904 100644 --- a/app/api/files.py +++ b/app/api/files.py @@ -460,7 +460,7 @@ def reprocess_with_cloud_ocr(request: Request, file_id: int, db: DbSession): def _extract_text_from_pdf(file_path: str) -> str: """ - Extract text from a PDF file using PyPDF2. + Extract text from a PDF file using pypdf. Args: file_path: Path to the PDF file @@ -468,11 +468,11 @@ def _extract_text_from_pdf(file_path: str) -> str: Returns: Extracted text from all pages """ - import PyPDF2 + import pypdf # Upgraded from PyPDF2 to fix CVE-2023-36464 extracted_text = "" with open(file_path, "rb") as f: - pdf_reader = PyPDF2.PdfReader(f) + pdf_reader = pypdf.PdfReader(f) for page in pdf_reader.pages: extracted_text += page.extract_text() + "\n" return extracted_text diff --git a/app/tasks/embed_metadata_into_pdf.py b/app/tasks/embed_metadata_into_pdf.py index fd8ec487..84dbbbff 100644 --- a/app/tasks/embed_metadata_into_pdf.py +++ b/app/tasks/embed_metadata_into_pdf.py @@ -7,7 +7,7 @@ import shutil import tempfile from pathlib import Path -import PyPDF2 # Replace fitz with PyPDF2 +import pypdf # Upgraded from PyPDF2 to fix CVE-2023-36464 # Import the shared Celery instance from app.celery_app import celery @@ -128,8 +128,8 @@ def embed_metadata_into_pdf(self, local_file_path: str, extracted_text: str, met # Open the PDF and modify metadata with open(processed_file, "rb") as file: - pdf_reader = PyPDF2.PdfReader(file) - pdf_writer = PyPDF2.PdfWriter() + pdf_reader = pypdf.PdfReader(file) + pdf_writer = pypdf.PdfWriter() # Copy all pages from the reader to the writer for page in pdf_reader.pages: diff --git a/app/tasks/process_document.py b/app/tasks/process_document.py index 44c0c179..aadc8e33 100644 --- a/app/tasks/process_document.py +++ b/app/tasks/process_document.py @@ -6,8 +6,8 @@ import os import shutil import uuid -import PyPDF2 # Replace fitz with PyPDF2 -from PyPDF2.errors import PdfReadError +import pypdf # Upgraded from PyPDF2 to fix CVE-2023-36464 +from pypdf.errors import PdfReadError from app.celery_app import celery from app.config import settings @@ -343,7 +343,7 @@ def process_document(self, original_local_file: str, original_filename: str = No ) try: with open(new_local_path, "rb") as file: - pdf_reader = PyPDF2.PdfReader(file) + pdf_reader = pypdf.PdfReader(file) has_text = False for page in pdf_reader.pages: if page.extract_text().strip(): @@ -391,7 +391,7 @@ def process_document(self, original_local_file: str, original_filename: str = No ) extracted_text = "" with open(new_local_path, "rb") as file: - pdf_reader = PyPDF2.PdfReader(file) + pdf_reader = pypdf.PdfReader(file) for page in pdf_reader.pages: extracted_text += page.extract_text() + "\n" diff --git a/app/tasks/process_with_azure_document_intelligence.py b/app/tasks/process_with_azure_document_intelligence.py index 9eab8d39..63aa0282 100644 --- a/app/tasks/process_with_azure_document_intelligence.py +++ b/app/tasks/process_with_azure_document_intelligence.py @@ -2,7 +2,7 @@ import logging import os import azure.core.exceptions -import PyPDF2 +import pypdf # Upgraded from PyPDF2 to fix CVE-2023-36464 from azure.ai.documentintelligence import DocumentIntelligenceClient from azure.ai.documentintelligence.models import AnalyzeOutputOption, AnalyzeResult from azure.core.credentials import AzureKeyCredential @@ -39,7 +39,7 @@ def get_pdf_page_count(file_path): """Get the number of pages in a PDF file.""" try: with open(file_path, "rb") as file: - pdf_reader = PyPDF2.PdfReader(file) + pdf_reader = pypdf.PdfReader(file) return len(pdf_reader.pages) except Exception as e: logger.error(f"Error getting PDF page count: {e}") diff --git a/app/tasks/rotate_pdf_pages.py b/app/tasks/rotate_pdf_pages.py index 4de2f55e..9b21435d 100644 --- a/app/tasks/rotate_pdf_pages.py +++ b/app/tasks/rotate_pdf_pages.py @@ -2,7 +2,7 @@ import json import logging import os -import PyPDF2 +import pypdf # Upgraded from PyPDF2 to fix CVE-2023-36464 from app.celery_app import celery from app.config import settings @@ -21,7 +21,7 @@ def determine_rotation_angle(detected_angle): detected_angle: The angle detected by Azure Document Intelligence Returns: - int: The angle to rotate the page in PyPDF2 (must be multiple of 90 degrees) + int: The angle to rotate the page in pypdf (must be multiple of 90 degrees) """ # Normalize angle to be between 0 and 360 normalized_angle = detected_angle % 360 @@ -35,15 +35,15 @@ def determine_rotation_angle(detected_angle): # For angles close to 90, 180, or 270 degrees (±5°), round to nearest 90° increment for target in [90, 180, 270]: if abs(normalized_angle - target) < 5: - # PyPDF2 uses clockwise rotation, so we need to use the complementary angle + # pypdf uses clockwise rotation, so we need to use the complementary angle rotation_value = (360 - target) % 360 logger.info(f"Detected angle {detected_angle}° is close to {target}°, will rotate by {rotation_value}°") return rotation_value # For other significant angles, round to nearest 90° increment - # (PyPDF2 only supports rotations in 90-degree increments) + # (pypdf only supports rotations in 90-degree increments) closest_90_multiple = round(normalized_angle / 90) * 90 - # Convert to PyPDF2 rotation value (clockwise) + # Convert to pypdf rotation value (clockwise) rotation_value = (360 - closest_90_multiple) % 360 logger.info(f"Detected angle {detected_angle}° rounded to {closest_90_multiple}°, will rotate by {rotation_value}°") return rotation_value @@ -110,8 +110,8 @@ def rotate_pdf_pages(self, filename: str, extracted_text: str, rotation_data=Non # Load the PDF with open(pdf_path, "rb") as file: - pdf_reader = PyPDF2.PdfReader(file) - pdf_writer = PyPDF2.PdfWriter() + pdf_reader = pypdf.PdfReader(file) + pdf_writer = pypdf.PdfWriter() # Process each page for page_idx in range(len(pdf_reader.pages)): @@ -123,7 +123,7 @@ def rotate_pdf_pages(self, filename: str, extracted_text: str, rotation_data=Non rotation_angle = determine_rotation_angle(detected_angle) if rotation_angle > 0: - # PyPDF2 uses clockwise rotation in 90-degree increments + # pypdf uses clockwise rotation in 90-degree increments page.rotate(rotation_angle) logger.info( f"[{task_id}] Page {page_idx+1} rotated by {rotation_angle}° " diff --git a/app/utils/file_splitting.py b/app/utils/file_splitting.py index ea501769..f4d08d37 100644 --- a/app/utils/file_splitting.py +++ b/app/utils/file_splitting.py @@ -10,7 +10,7 @@ import logging import os from typing import List, Optional -from PyPDF2 import PdfReader, PdfWriter +from pypdf import PdfReader, PdfWriter # Upgraded from PyPDF2 to fix CVE-2023-36464 logger = logging.getLogger(__name__) diff --git a/app/views/files.py b/app/views/files.py index 862bb2e3..9ec97380 100644 --- a/app/views/files.py +++ b/app/views/files.py @@ -492,8 +492,8 @@ def get_original_text(request: Request, file_id: int, db: Session = Depends(get_ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Original file not found on disk") try: - # Extract text from PDF using PyPDF2 - from PyPDF2 import PdfReader + # Extract text from PDF using pypdf + from pypdf import PdfReader # Upgraded from PyPDF2 to fix CVE-2023-36464 reader = PdfReader(file_record.original_file_path) text = "" @@ -532,8 +532,8 @@ def get_processed_text(request: Request, file_id: int, db: Session = Depends(get raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Processed file not found on disk") try: - # Extract text from PDF using PyPDF2 - from PyPDF2 import PdfReader + # Extract text from PDF using pypdf + from pypdf import PdfReader # Upgraded from PyPDF2 to fix CVE-2023-36464 reader = PdfReader(file_record.processed_file_path) text = "" diff --git a/requirements.txt b/requirements.txt index 2fffd659..1f3ede9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ sqlalchemy # Database ORM pydantic # Data validation cryptography>=41.0.0 # Encryption for sensitive settings in database openai # GPT integration for metadata extraction -PyPDF2>=3.0.0 # PDF processing for text extraction, metadata editing and rotation (replaces PyMuPDF) +pypdf>=3.9.0 # PDF processing for text extraction, metadata editing and rotation (upgraded from PyPDF2 to fix CVE-2023-36464) requests # HTTP client puremagic>=1.25,<2.0 # File type detection (pure Python) filetype>=1.2.0,<2.0 # File type detection fallback (pure Python) diff --git a/tests/test_external_integrations.py b/tests/test_external_integrations.py index 669fd9aa..30c0166d 100644 --- a/tests/test_external_integrations.py +++ b/tests/test_external_integrations.py @@ -778,7 +778,7 @@ class TestPdfGenerator: @staticmethod def test_generate_default_pdf() -> None: """Test that generate_test_pdf creates a valid PDF with embedded text.""" - import PyPDF2 + import pypdf # Upgraded from PyPDF2 to fix CVE-2023-36464 path = generate_test_pdf() try: @@ -786,7 +786,7 @@ class TestPdfGenerator: assert os.path.getsize(path) > 100 with open(path, "rb") as f: - reader = PyPDF2.PdfReader(f) + reader = pypdf.PdfReader(f) assert len(reader.pages) >= 1 text = reader.pages[0].extract_text() assert "Invoice" in text @@ -797,13 +797,13 @@ class TestPdfGenerator: @staticmethod def test_generate_custom_content_pdf() -> None: """Test that generate_test_pdf accepts custom content.""" - import PyPDF2 + import pypdf # Upgraded from PyPDF2 to fix CVE-2023-36464 custom = "Custom test content for verification" path = generate_test_pdf(content=custom) try: with open(path, "rb") as f: - reader = PyPDF2.PdfReader(f) + reader = pypdf.PdfReader(f) text = reader.pages[0].extract_text() assert "Custom test content" in text finally: diff --git a/tests/test_file_splitting.py b/tests/test_file_splitting.py index 1ab04c2b..f88460da 100644 --- a/tests/test_file_splitting.py +++ b/tests/test_file_splitting.py @@ -12,7 +12,7 @@ import os import tempfile import pytest -from PyPDF2 import PdfReader, PdfWriter +from pypdf import PdfReader, PdfWriter # Upgraded from PyPDF2 to fix CVE-2023-36464 from app.utils.file_splitting import should_split_file, split_pdf_by_size