{"record":{"id":"614296aea70f39e0","repo":"binary-husky/gpt_academic","slug":"path-or-url","errorCode":null,"errorMessage":"输入文件的路径 ({path_or_url}) 存在，但位置非法。请将文件上传后再执行该任务。","messagePattern":"输入文件的路径 \\((.+?)\\) 存在，但位置非法。请将文件上传后再执行该任务。","errorType":"exception","errorClass":"FriendlyException","httpStatus":null,"severity":"error","filePath":"shared_utils/fastapi_server.py","lineNumber":63,"sourceCode":"\"\"\"\n\nimport os, requests, threading, time\nimport uvicorn\n\ndef validate_path_safety(path_or_url, user):\n    from toolbox import get_conf, default_user_name\n    from toolbox import FriendlyException\n    PATH_PRIVATE_UPLOAD, PATH_LOGGING = get_conf('PATH_PRIVATE_UPLOAD', 'PATH_LOGGING')\n    sensitive_path = None\n    path_or_url = os.path.relpath(path_or_url)\n    if path_or_url.startswith(PATH_LOGGING):    # 日志文件（按用户划分）\n        sensitive_path = PATH_LOGGING\n    elif path_or_url.startswith(PATH_PRIVATE_UPLOAD):   # 用户的上传目录（按用户划分）\n        sensitive_path = PATH_PRIVATE_UPLOAD\n    elif path_or_url.startswith('tests') or path_or_url.startswith('build'):   # 一个常用的测试目录\n        return True\n    else:\n        raise FriendlyException(f\"输入文件的路径 ({path_or_url}) 存在，但位置非法。请将文件上传后再执行该任务。\") # return False\n    if sensitive_path:\n        allowed_users = [user, 'autogen', 'arxiv_cache', default_user_name]  # three user path that can be accessed\n        for user_allowed in allowed_users:\n            if f\"{os.sep}\".join(path_or_url.split(os.sep)[:2]) == os.path.join(sensitive_path, user_allowed):\n                return True\n        raise FriendlyException(f\"输入文件的路径 ({path_or_url}) 存在，但属于其他用户。请将文件上传后再执行该任务。\") # return False\n    return True\n\ndef _authorize_user(path_or_url, request, gradio_app):\n    from toolbox import get_conf, default_user_name\n    PATH_PRIVATE_UPLOAD, PATH_LOGGING = get_conf('PATH_PRIVATE_UPLOAD', 'PATH_LOGGING')\n    sensitive_path = None\n    path_or_url = os.path.relpath(path_or_url)\n    if path_or_url.startswith(PATH_LOGGING):\n        sensitive_path = PATH_LOGGING\n    if path_or_url.startswith(PATH_PRIVATE_UPLOAD):\n        sensitive_path = PATH_PRIVATE_UPLOAD\n    if sensitive_path:","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/binary-husky/gpt_academic/blob/d6bde0fa54373309bd05823a49bda8da019d2c77/shared_utils/fastapi_server.py#L45-L81","documentation":"FastAPI layer's path validation (_check_path_at_legal_server_side): after normalizing to a relative path, only three roots are allowed — PATH_LOGGING, PATH_PRIVATE_UPLOAD, and tests/build. Any existing file outside these roots (e.g. /etc/passwd or an arbitrary repo file) raises FriendlyException telling the user to upload the file first. It is an anti-arbitrary-file-read guard for the API server.","triggerScenarios":"Calling an API endpoint that takes a file path with a path outside the allowed roots; e.g. POST /v1/api/... with input_file pointing at a system file or a project source file instead of something under the per-user upload or logging directory.","commonSituations":"Scripting against the fastapi_server with absolute local paths; porting curl examples from local CLI usage to the served API; attempting path traversal ('..') which normalizes outside the roots and hits this branch.","solutions":["Upload the file through the API/WebUI so it lands under PATH_PRIVATE_UPLOAD/<user>/ and pass that path.","Reference files under the tests/ or build/ directory if you genuinely need repo-local fixtures.","For log-derived files, use paths under PATH_LOGGING belonging to your user."],"exampleFix":"# before\ncurl -X POST .../v1/api/chat -d '{\"input_file\": \"/etc/passwd\"}'\n\n# after\ncurl -X POST .../v1/api/upload -F 'file=@doc.pdf'   # then pass returned private-upload path","handlingStrategy":"validation","validationCode":"import os\nfrom toolbox import get_conf, default_user_name\nPATH_PRIVATE_UPLOAD, PATH_LOGGING = get_conf('PATH_PRIVATE_UPLOAD', 'PATH_LOGGING')\n\ndef path_is_api_legal(p: str, user: str) -> bool:\n    rp = os.path.relpath(p)\n    return (rp.startswith(PATH_LOGGING) or rp.startswith(PATH_PRIVATE_UPLOAD)\n            or rp.startswith('tests') or rp.startswith('build'))\n\nif not path_is_api_legal(input_file, user):\n    input_file = upload_file_first(input_file)  # route through the upload API","typeGuard":null,"tryCatchPattern":"try:\n    run_api_task(input_file=...)\nexcept FriendlyException as e:\n    if '位置非法' in str(e):\n        uploaded = upload_and_get_private_path(local_file)\n        run_api_task(input_file=uploaded)","preventionTips":["Never pass absolute system paths to the served API; always upload first.","Keep API-referenced files under the per-user upload directory.","Treat this exception as a security boundary, not an inconvenience."],"tags":["security","path-validation","fastapi","file-upload","traversal"],"backgroundTag":null,"analyzedSha":"d6bde0fa54373309bd05823a49bda8da019d2c77","analyzedAt":"2026-08-14T22:48:35.038Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}