{"record":{"id":"ae7a2b41dedd137e","repo":"OpenBMB/ChatDev","slug":"invalid-session-id-only-letters-digits-undersco","errorCode":null,"errorMessage":"Invalid session_id: only letters, digits, underscores, and hyphens are allowed","messagePattern":"Invalid session_id: only letters, digits, underscores, and hyphens are allowed","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"warning","filePath":"server/routes/sessions.py","lineNumber":27,"sourceCode":"\nfrom server.settings import WARE_HOUSE_DIR\nfrom utils.exceptions import ResourceNotFoundError, ValidationError\nfrom utils.structured_logger import get_server_logger, LogType\n\nrouter = APIRouter()\n\n\n@router.get(\"/api/sessions/{session_id}/download\")\nasync def download_session(session_id: str):\n    try:\n        if not re.match(r\"^[a-zA-Z0-9_-]+$\", session_id):\n            logger = get_server_logger()\n            logger.log_security_event(\n                \"INVALID_SESSION_ID_FORMAT\",\n                f\"Invalid session_id format: {session_id}\",\n                details={\"received_session_id\": session_id},\n            )\n            raise ValidationError(\n                \"Invalid session_id: only letters, digits, underscores, and hyphens are allowed\",\n                field=\"session_id\",\n            )\n\n        dir_name = f\"session_{session_id}\"\n        session_path = WARE_HOUSE_DIR / dir_name\n\n        if not session_path.exists() or not session_path.is_dir():\n            raise ResourceNotFoundError(\n                \"Session directory not found\",\n                resource_type=\"session\",\n                resource_id=session_id,\n            )\n\n        with tempfile.NamedTemporaryFile(delete=False, suffix=\".zip\") as tmp_file:\n            zip_path = Path(tmp_file.name)\n\n        archive_base = zip_path.with_suffix(\"\")","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/OpenBMB/ChatDev/blob/4fb2db0ea90375ce1059f44fe03ffbd191a7a169/server/routes/sessions.py#L9-L45","documentation":"download_session validates session_id against an allowlist (letters, digits, underscores, hyphens) and logs a security event INVALID_SESSION_ID_FORMAT before raising ValidationError. This is a path-traversal guard for the warehouse directory lookup.","triggerScenarios":"GET session download with session_id containing slashes, dots (../), spaces, percent-encoded separators, or any character outside [A-Za-z0-9_-].","commonSituations":"Passing a full directory name like session_abc123 instead of the bare ID; unencoded special characters from URLs; attempted or accidental path traversal strings.","solutions":["Strip any 'session_' prefix and slashes from the ID before calling","Use only the exact session_id returned when the session was created","URL-encode the path segment properly and avoid raw ../ sequences","Add a client-side regex check: ^[A-Za-z0-9_-]+$"],"exampleFix":"# before\nsession_id = \"session_abc123/..\"\n# after\nsession_id = \"abc123\"","handlingStrategy":"type-guard","validationCode":"import re\nSESSION_ID_RE = re.compile(r'^[A-Za-z0-9_-]+$')\nif not SESSION_ID_RE.match(session_id):\n    raise ValueError('bad session_id format')","typeGuard":"def is_safe_session_id(sid: str) -> bool:\n    import re\n    return bool(re.fullmatch(r'[A-Za-z0-9_-]+', sid))","tryCatchPattern":"except HTTPError as e:\n    if e.response.status_code == 400 and 'Invalid session_id' in e.response.text:\n        session_id = extract_raw_id(session_id); retry()","preventionTips":["Never construct session IDs from user filenames","Strip 'session_' prefixes and path separators client-side"],"tags":["security","validation","path-traversal","session"],"backgroundTag":"input-validation-failed","analyzedSha":"4fb2db0ea90375ce1059f44fe03ffbd191a7a169","analyzedAt":"2026-08-27T14:35:29.622Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}