Skip to content

feat(backend): add conversation export and import API (#1808)#3176

Merged
mrveiss merged 1 commit intoDev_new_guifrom
feat/conversation-export-import-1808
Apr 1, 2026
Merged

feat(backend): add conversation export and import API (#1808)#3176
mrveiss merged 1 commit intoDev_new_guifrom
feat/conversation-export-import-1808

Conversation

@mrveiss
Copy link
Copy Markdown
Owner

@mrveiss mrveiss commented Apr 1, 2026

Summary

  • Add services/conversation_export.py: JSON and Markdown single-session export, bulk JSON archive, JSON import with skip/replace/rename conflict resolution
  • Add api/conversation_export.py: GET /conversations/{id}/export?format=json|markdown, GET /conversations/export-all, POST /conversations/import
  • Register as optional feature router in feature_routers.py

Closes #1808

Test plan

  • Export single conversation as JSON — verify structure and metadata
  • Export single conversation as Markdown — verify human-readable format
  • Export all conversations — verify bulk archive
  • Import JSON — verify round-trip fidelity
  • Import duplicate with skip/replace/rename conflict modes

🤖 Generated with Claude Code

Add JSON/Markdown single-session export, bulk JSON archive, and JSON
round-trip import with skip/replace/rename conflict resolution.
Register as optional feature router.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mrveiss mrveiss merged commit 692ac98 into Dev_new_gui Apr 1, 2026
2 of 4 checks passed
@mrveiss mrveiss deleted the feat/conversation-export-import-1808 branch April 1, 2026 18:33
)
except ValueError:
continue
if not os.path.exists(chat_file):

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI about 5 hours ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

continue
if not os.path.exists(chat_file):
continue
async with aiofiles.open(chat_file, "r", encoding="utf-8") as fh:

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI about 5 hours ago

At a high level, we should ensure that user-controlled session_id cannot produce arbitrary filenames even within the trusted chats_directory. The simplest robust fix is to sanitize or validate session_id locally in _load_full_session_data and reject values that contain characters which could change the intended naming pattern (path separators, traversal sequences, wildcards, etc.). This is in addition to the existing validate_relative_path protection, giving a clear guarantee that only simple, expected session IDs are used to construct filenames.

Best targeted fix without changing existing functionality:

  • Inside _load_full_session_data (in autobot-backend/services/conversation_export.py), before constructing filename_template, normalize and validate the session_id.
  • Enforce a strict pattern for session_id at this layer, for example allowing only ASCII letters, digits, underscore, and hyphen. This matches typical session or UUID formats and should not break legitimate usage.
  • If session_id fails this check, log a warning and return None (behaving as “session not found”), so the API continues to raise a ResourceNotFoundError as it already does when _load_full_session_data returns None.
  • Implement the check using the standard library only (e.g., re), to avoid new dependencies.

Concretely:

  • Add import re at the top of autobot-backend/services/conversation_export.py.

  • In _load_full_session_data, right after entering the try block (before getting chats_directory and before building filename_template), add a small validation snippet:

    if not re.fullmatch(r"[A-Za-z0-9_-]+", session_id):
        logger.warning("Rejected session_id with invalid characters: %r", session_id)
        return None

This ensures filename_template stays a simple filename, eliminating any chance to smuggle directory separators or similar, and should satisfy CodeQL since the path now depends on a strictly validated ID.


Suggested changeset 1
autobot-backend/services/conversation_export.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/autobot-backend/services/conversation_export.py b/autobot-backend/services/conversation_export.py
--- a/autobot-backend/services/conversation_export.py
+++ b/autobot-backend/services/conversation_export.py
@@ -15,6 +15,7 @@
 import logging
 import time
 from typing import Any, Dict, List, Optional, Tuple
+import re
 
 logger = logging.getLogger(__name__)
 
@@ -271,6 +272,15 @@
     Returns None when the session does not exist or loading fails.
     """
     try:
+        # Defensive validation: ensure session_id cannot influence directory
+        # structure or introduce unexpected characters in filenames.
+        if not re.fullmatch(r"[A-Za-z0-9_-]+", session_id):
+            logger.warning(
+                "Rejected session_id with invalid characters for file access: %r",
+                session_id,
+            )
+            return None
+
         chats_directory = chat_history_manager._get_chats_directory()
         import os
 
EOF
@@ -15,6 +15,7 @@
import logging
import time
from typing import Any, Dict, List, Optional, Tuple
import re

logger = logging.getLogger(__name__)

@@ -271,6 +272,15 @@
Returns None when the session does not exist or loading fails.
"""
try:
# Defensive validation: ensure session_id cannot influence directory
# structure or introduce unexpected characters in filenames.
if not re.fullmatch(r"[A-Za-z0-9_-]+", session_id):
logger.warning(
"Rejected session_id with invalid characters for file access: %r",
session_id,
)
return None

chats_directory = chat_history_manager._get_chats_directory()
import os

Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants