{"record":{"id":"e35493fd2beb0d4b","repo":"datawhalechina/hello-agents","slug":"absolute-paths-are-not-allowed-rel-path","errorCode":null,"errorMessage":"Absolute paths are not allowed: {rel_path}","messagePattern":"Absolute paths are not allowed: (.+?)","errorType":"exception","errorClass":"PatchApplyError","httpStatus":null,"severity":"error","filePath":"Co-creation-projects/YYHDBL-HelloCodeAgentCli/code_agent/executors/apply_patch_executor.py","lineNumber":200,"sourceCode":"        # 返回最终的应用结果\n        return ApplyResult(files_changed=files_changed, backups=backups)\n\n    def _safe_path(self, rel_path: str) -> Path:\n        \"\"\"\n        验证路径安全性，防止路径遍历攻击 (Path Traversal)。\n        确保目标路径在 repo_root 目录下，防止访问仓库外的文件。\n        \n        参数:\n            rel_path: 相对路径字符串\n            \n        返回:\n            Path: 安全的绝对路径对象\n            \n        异常:\n            PatchApplyError: 当路径是绝对路径、包含特殊字符或试图访问仓库外时抛出\n        \"\"\"\n        if rel_path.startswith(\"/\") or rel_path.startswith(\"~\"):\n            raise PatchApplyError(f\"Absolute paths are not allowed: {rel_path}\")\n        target = (self.repo_root / rel_path).resolve()\n        # 检查解析后的路径是否以 repo_root 开头\n        if not str(target).startswith(str(self.repo_root.resolve()) + os.sep) and target != self.repo_root.resolve():\n            raise PatchApplyError(f\"Path escapes repo_root: {rel_path}\")\n        if target.exists() and target.is_symlink():\n            raise PatchApplyError(f\"Refusing to modify symlink: {rel_path}\")\n        return target\n\n    def _enforce_suffix(self, target: Path) -> None:\n        \"\"\"\n        检查目标文件的后缀是否在允许的列表中。\n        防止意外修改二进制文件、配置文件或其他敏感文件。\n        \n        参数:\n            target: 目标文件路径对象\n            \n        异常:\n            PatchApplyError: 当文件后缀不在允许列表中时抛出","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/YYHDBL-HelloCodeAgentCli/code_agent/executors/apply_patch_executor.py#L182-L218","documentation":"Raised by _safe_path when the rel_path argument begins with '/' or '~', i.e. the caller passed an absolute POSIX path or a home-relative path instead of a path relative to repo_root. The executor deliberately confines all writes to repo_root, so absolute targets are rejected before any filesystem access.","triggerScenarios":"Passing '/etc/passwd', '~/notes.md', or any path starting with '/' as the file path in an '*** Add File: ' / '*** Update File: ' section of the patch; LLM-generated patches that emit absolute paths copied from tool output.","commonSituations":"Agent models echoing absolute paths from a previous tool result; patches authored on a different machine with absolute paths; scripts reusing a resolved Path object where a relative string is expected.","solutions":["Strip the repo_root prefix (or re-relativize) before building the patch: rel = os.path.relpath(abs_path, repo_root).","Fix the patch text so file headers use repo-relative paths like 'src/main.py'.","If the intent really is to write outside the repo, instantiate the executor with a repo_root that contains the target, rather than bypassing the guard."],"exampleFix":"# before\n'*** Update File: /home/user/proj/src/main.py'\n\n# after\nfrom pathlib import Path\nrel = Path(abs_path).relative_to(repo_root).as_posix()  # 'src/main.py'\n# use '*** Update File: src/main.py' in the patch","handlingStrategy":"validation","validationCode":"def to_rel(abs_or_rel: str, repo_root: Path) -> str:\n    p = Path(abs_or_rel)\n    if p.is_absolute() or abs_or_rel.startswith('~'):\n        p = Path(os.path.expanduser(abs_or_rel))\n        return p.resolve().relative_to(repo_root.resolve()).as_posix()\n    return abs_or_rel\n\nrel = to_rel(model_path, repo_root)\nassert not rel.startswith(('/', '~')), 'must be repo-relative'","typeGuard":"def is_repo_relative(rel_path: str) -> bool:\n    return not rel_path.startswith('/') and not rel_path.startswith('~')","tryCatchPattern":"try:\n    executor.apply(patch)\nexcept PatchApplyError as e:\n    if 'Absolute paths are not allowed' in str(e):\n        patch = rewrite_paths_relative(patch, repo_root)\n        executor.apply(patch)\n    else:\n        raise","preventionTips":["Always emit repo-relative posix paths in patch file headers.","Relativize any absolute path from tool output before embedding it in a patch.","Unit-test the patch builder against absolute-path leakage."],"tags":["patch","path-traversal","security","apply-patch"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}