diff --git a/backend/app/api/routes/archives.py b/backend/app/api/routes/archives.py index 6ca4546cf..83dd13ed7 100644 --- a/backend/app/api/routes/archives.py +++ b/backend/app/api/routes/archives.py @@ -1833,7 +1833,8 @@ async def get_archive_capabilities( ): """Check what viewing capabilities are available for this 3MF file.""" import json - import xml.etree.ElementTree as ET + + import defusedxml.ElementTree as ET service = ArchiveService(db) archive = await service.get_archive(archive_id) @@ -2113,7 +2114,7 @@ async def get_plate_preview( plate_num = 1 if "Metadata/slice_info.config" in names: try: - import xml.etree.ElementTree as ET + import defusedxml.ElementTree as ET slice_content = zf.read("Metadata/slice_info.config").decode("utf-8") root = ET.fromstring(slice_content) @@ -2254,7 +2255,8 @@ async def get_archive_plates( """ import json import re - import xml.etree.ElementTree as ET + + import defusedxml.ElementTree as ET service = ArchiveService(db) archive = await service.get_archive(archive_id) @@ -2560,7 +2562,7 @@ async def get_filament_requirements( archive_id: The archive ID plate_id: Optional plate index to filter filaments for (for multi-plate files) """ - import xml.etree.ElementTree as ET + import defusedxml.ElementTree as ET service = ArchiveService(db) archive = await service.get_archive(archive_id) diff --git a/backend/app/api/routes/library.py b/backend/app/api/routes/library.py index 1bd13a615..785244627 100644 --- a/backend/app/api/routes/library.py +++ b/backend/app/api/routes/library.py @@ -1312,9 +1312,10 @@ async def get_library_file_plates( """ import json import re - import xml.etree.ElementTree as ET import zipfile + import defusedxml.ElementTree as ET + # Get the library file result = await db.execute(select(LibraryFile).where(LibraryFile.id == file_id)) lib_file = result.scalar_one_or_none() @@ -1613,9 +1614,10 @@ async def get_library_file_filament_requirements( file_id: The library file ID plate_id: Optional plate index to get filaments for a specific plate """ - import xml.etree.ElementTree as ET import zipfile + import defusedxml.ElementTree as ET + # Get the library file result = await db.execute(select(LibraryFile).where(LibraryFile.id == file_id)) lib_file = result.scalar_one_or_none() diff --git a/backend/app/api/routes/print_queue.py b/backend/app/api/routes/print_queue.py index f6180abd2..7683e6075 100644 --- a/backend/app/api/routes/print_queue.py +++ b/backend/app/api/routes/print_queue.py @@ -2,11 +2,11 @@ import json import logging -import xml.etree.ElementTree as ET import zipfile from datetime import datetime from pathlib import Path +import defusedxml.ElementTree as ET from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession diff --git a/backend/app/api/routes/printers.py b/backend/app/api/routes/printers.py index b2ff3b470..2dbcc9e38 100644 --- a/backend/app/api/routes/printers.py +++ b/backend/app/api/routes/printers.py @@ -852,9 +852,10 @@ async def get_printer_file_plates( """Get available plates from a multi-plate 3MF file stored on a printer.""" import io import json - import xml.etree.ElementTree as ET import zipfile + import defusedxml.ElementTree as ET + # Validate printer result = await db.execute(select(Printer).where(Printer.id == printer_id)) printer = result.scalar_one_or_none() diff --git a/backend/app/services/archive.py b/backend/app/services/archive.py index 39037e703..352d142dc 100644 --- a/backend/app/services/archive.py +++ b/backend/app/services/archive.py @@ -6,8 +6,8 @@ import shutil import zipfile from datetime import datetime from pathlib import Path -from xml.etree import ElementTree as ET +from defusedxml import ElementTree as ET from sqlalchemy import and_, or_, select from sqlalchemy.ext.asyncio import AsyncSession diff --git a/backend/app/services/print_scheduler.py b/backend/app/services/print_scheduler.py index aff3818ac..6dbd8e6ff 100644 --- a/backend/app/services/print_scheduler.py +++ b/backend/app/services/print_scheduler.py @@ -3,11 +3,11 @@ import asyncio import json import logging -import xml.etree.ElementTree as ET import zipfile from datetime import datetime, timedelta from pathlib import Path +import defusedxml.ElementTree as ET from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession diff --git a/requirements.txt b/requirements.txt index 682aea915..bc8c2eba9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,6 +20,7 @@ pyftpdlib>=2.0.0 cryptography>=41.0.0 # 3MF Processing (standard zipfile is sufficient for Bambu 3MF files) +defusedxml>=0.7.0 # Safe XML parsing (prevents XXE attacks) # Excel Export openpyxl>=3.1.0