Merge branch 'main' into feat/agent-node-v2

This commit is contained in:
Novice
2025-12-16 15:17:29 +08:00
142 changed files with 16019 additions and 1431 deletions

View File

@ -8,6 +8,7 @@ from sqlalchemy import or_, select
from werkzeug.datastructures import FileStorage
from werkzeug.exceptions import NotFound
from core.helper.csv_sanitizer import CSVSanitizer
from extensions.ext_database import db
from extensions.ext_redis import redis_client
from libs.datetime_utils import naive_utc_now
@ -158,6 +159,12 @@ class AppAnnotationService:
@classmethod
def export_annotation_list_by_app_id(cls, app_id: str):
"""
Export all annotations for an app with CSV injection protection.
Sanitizes question and content fields to prevent formula injection attacks
when exported to CSV format.
"""
# get app info
_, current_tenant_id = current_account_with_tenant()
app = (
@ -174,6 +181,16 @@ class AppAnnotationService:
.order_by(MessageAnnotation.created_at.desc())
.all()
)
# Sanitize CSV-injectable fields to prevent formula injection
for annotation in annotations:
# Sanitize question field if present
if annotation.question:
annotation.question = CSVSanitizer.sanitize_value(annotation.question)
# Sanitize content field (answer)
if annotation.content:
annotation.content = CSVSanitizer.sanitize_value(annotation.content)
return annotations
@classmethod

View File

@ -1419,7 +1419,7 @@ class DocumentService:
document.name = name
db.session.add(document)
if document.data_source_info_dict:
if document.data_source_info_dict and "upload_file_id" in document.data_source_info_dict:
db.session.query(UploadFile).where(
UploadFile.id == document.data_source_info_dict["upload_file_id"]
).update({UploadFile.name: name})

View File

@ -33,6 +33,11 @@ from services.errors.app import QuotaExceededError
from services.trigger.app_trigger_service import AppTriggerService
from services.workflow.entities import WebhookTriggerData
try:
import magic
except ImportError:
magic = None # type: ignore[assignment]
logger = logging.getLogger(__name__)
@ -317,7 +322,8 @@ class WebhookService:
try:
file_content = request.get_data()
if file_content:
file_obj = cls._create_file_from_binary(file_content, "application/octet-stream", webhook_trigger)
mimetype = cls._detect_binary_mimetype(file_content)
file_obj = cls._create_file_from_binary(file_content, mimetype, webhook_trigger)
return {"raw": file_obj.to_dict()}, {}
else:
return {"raw": None}, {}
@ -341,6 +347,18 @@ class WebhookService:
body = {"raw": ""}
return body, {}
@staticmethod
def _detect_binary_mimetype(file_content: bytes) -> str:
"""Guess MIME type for binary payloads using python-magic when available."""
if magic is not None:
try:
detected = magic.from_buffer(file_content[:1024], mime=True)
if detected:
return detected
except Exception:
logger.debug("python-magic detection failed for octet-stream payload")
return "application/octet-stream"
@classmethod
def _process_file_uploads(
cls, files: Mapping[str, FileStorage], webhook_trigger: WorkflowWebhookTrigger