{"record":{"id":"276fa9106dd3c16b","repo":"bytedance/deer-flow","slug":"e","errorCode":null,"errorMessage":"{e}","messagePattern":"\\{e\\}","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"backend/app/gateway/path_utils.py","lineNumber":32,"sourceCode":"    Args:\n        thread_id: The thread ID.\n        virtual_path: The virtual path as seen inside the sandbox\n                      (e.g., /mnt/user-data/outputs/file.txt).\n        user_id: The user whose storage to resolve under. Defaults to the\n                 effective user when not given; callers acting on behalf of a\n                 specific owner (e.g. trusted internal callers) pass it explicitly.\n\n    Returns:\n        The resolved filesystem path.\n\n    Raises:\n        HTTPException: If the path is invalid or outside allowed directories.\n    \"\"\"\n    try:\n        return get_paths().resolve_virtual_path(thread_id, virtual_path, user_id=user_id or get_effective_user_id())\n    except ValueError as e:\n        status = 403 if \"traversal\" in str(e) else 400\n        raise HTTPException(status_code=status, detail=str(e))\n","sourceCodeStart":14,"sourceCodeEnd":33,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/path_utils.py#L14-L33","documentation":"Wraps `ValueError` from `resolve_virtual_path` into an HTTPException: 403 when the message contains 'traversal' (path escapes allowed directories), otherwise 400 for other invalid path input. The detail text is the original ValueError message, so the underlying cause is visible to the caller.","triggerScenarios":"Passing a `virtual_path` containing `..` segments that resolve outside the thread/user scope; supplying an empty or malformed path; providing a `thread_id` that is invalid or unknown to the path resolver; a user_id mismatch when resolving another user's paths without explicit trusted-caller override.","commonSituations":"Client-side joined file paths leaking absolute paths or parent segments; agents echoing user-supplied filenames into path APIs; a thread_id from a different/older deployment being reused after data was cleared.","solutions":["Sanitize the virtual path client-side: strip leading slashes, reject `..` segments, keep it relative to the thread root","Verify the thread_id exists and belongs to the caller before issuing file calls","If you are an internal trusted caller, pass the explicit `user_id` parameter instead of relying on ambient resolution","Read the returned detail message — it states exactly which constraint (traversal vs invalid) failed"],"exampleFix":"// before\nconst p = userInput; // e.g. '../../secrets.env'\nawait api.writeFile(threadId, p, data);\n// after\nconst safe = userInput.replace(/\\\\..\\\\/g, '');\nif (userInput.includes('..')) throw new Error('bad path');\nawait api.writeFile(threadId, safe, data);","handlingStrategy":"validation","validationCode":"function isSafeVirtualPath(p: string): boolean {\n  return p !== '' && !p.startsWith('/') && !p.split('/').includes('..') && !p.includes('\\\\');\n}\nif (!isSafeVirtualPath(virtualPath)) throw new Error(`unsafe path: ${virtualPath}`);","typeGuard":"const isSafeVirtualPath = (p: string): boolean =>\n  typeof p === 'string' && p.length > 0 && !p.startsWith('/') &&\n  p.split('/').every((seg) => seg !== '..' && seg !== '.' && !seg.includes('\\\\'));","tryCatchPattern":"try {\n  await api.writeFile(threadId, vpath, data);\n} catch (e) {\n  if (e.status === 403 && /traversal/i.test(e.detail)) throw new UserError('path escapes allowed dirs');\n  if (e.status === 400) throw new UserError(`invalid path: ${e.detail}`);\n  throw e;\n}","preventionTips":["Always build virtual paths from sanitized, user-independent components","Reject '..' and absolute paths before any file API call","Verify thread ownership before issuing per-thread file operations"],"tags":["filesystem","path-traversal","http-403","http-400","validation"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}