{"record":{"id":"90db115f4894f72a","repo":"Panniantong/Agent-Reach","slug":"label-current","errorCode":null,"errorMessage":"{label}不能经过符号链接：{current}","messagePattern":"(.+?)不能经过符号链接：(.+?)","errorType":"exception","errorClass":"ConfigSecurityError","httpStatus":null,"severity":"critical","filePath":"agent_reach/config.py","lineNumber":43,"sourceCode":"\n\nclass ConfigError(RuntimeError):\n    \"\"\"Base class for configuration errors safe to show to the user.\"\"\"\n\n\nclass ConfigReadOnlyError(ConfigError):\n    \"\"\"Raised when code tries to mutate an explicitly read-only config.\"\"\"\n\n\nclass ConfigSecurityError(ConfigError):\n    \"\"\"Raised when a config path could redirect credential reads or writes.\"\"\"\n\n\ndef _reject_symlink(path: Path, label: str) -> None:\n    try:\n        ensure_no_symlink_path(path, label)\n    except PrivatePathError as exc:\n        raise ConfigSecurityError(str(exc)) from exc\n\n\ndef _atomic_write_yaml(target: Path, data: dict) -> None:\n    \"\"\"Atomically replace ``target`` with owner-only YAML.\n\n    The temporary file lives beside the target so ``os.replace`` remains an\n    atomic same-filesystem operation. Existing symlinks are rejected rather\n    than followed or silently replaced.\n    \"\"\"\n    _reject_symlink(target, \"配置文件\")\n    fd, tmp_name = tempfile.mkstemp(\n        dir=str(target.parent),\n        prefix=f\".{target.name}.\",\n        suffix=\".tmp\",\n    )\n    tmp_path = Path(tmp_name)\n    try:\n        if os.name != \"nt\" and hasattr(os, \"fchmod\"):","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/Panniantong/Agent-Reach/blob/93ae1d18c37b707dec053c7c4f9d91cd8ef8943d/agent_reach/config.py#L25-L61","documentation":"Config writes are hardened against symlink redirection: before atomically writing the YAML (in _atomic_write_yaml, agent_reach/config.py:43→53), the target config file path is checked with ensure_no_symlink_path. If the path to ~/.agent-reach/config.yaml traverses or ends at a symlink, a ConfigSecurityError (subclass of ConfigError/RuntimeError) is raised with the Chinese message '<label>不能经过符号链接：<path>' ('the config file path must not pass through a symlink'). This prevents an attacker from redirecting credential writes.","triggerScenarios":"Config.save() (or any `agent-reach configure` write) when ~/.agent-reach/config.yaml is a symlink to another location, or when ~/.agent-reach itself is a symlink (that case is labeled 配置目录 instead). Dotfile managers (stow, chezmoi, mackup) commonly create such symlinks.","commonSituations":"Users managing dotfiles with GNU stow or mackup symlinking ~/.agent-reach into a repo; multi-user setups sharing one config via symlink; sandboxed environments that symlink home subdirectories.","solutions":["Replace the symlink with the real file/dir: move the actual config to ~/.agent-reach/config.yaml and remove the symlink","If using a dotfile manager, exclude ~/.agent-reach from stow/mackup management, or have the manager copy instead of link","Check every path component: ls -la ~/.agent-reach/ and readlink -f ~/.agent-reach/config.yaml to find the offending link","If sharing across machines is the goal, copy the file explicitly (scp/rsync) rather than linking"],"exampleFix":"# before: symlink-managed config\n~/.agent-reach/config.yaml -> ~/dotfiles/agent-reach/config.yaml\n\n# after: real file (sync by copying, not linking)\nrm ~/.agent-reach/config.yaml\ncp ~/dotfiles/agent-reach/config.yaml ~/.agent-reach/config.yaml\nchmod 600 ~/.agent-reach/config.yaml","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef config_path_is_safe(base: Path) -> bool:\n    \"\"\"True when no component of the config file path is a symlink.\"\"\"\n    cur = base\n    while True:\n        if cur.is_symlink():\n            return False\n        if cur == cur.parent:\n            return True\n        cur = cur.parent\n\nassert config_path_is_safe(Path.home() / \".agent-reach\" / \"config.yaml\")","typeGuard":null,"tryCatchPattern":"from agent_reach.config import Config, ConfigSecurityError\n\ntry:\n    cfg = Config(); cfg.save()\nexcept ConfigSecurityError as exc:\n    if \"符号链接\" in str(exc):\n        repair_symlinked_config()  # convert link to real file, then retry\n    else:\n        raise","preventionTips":["Never let dotfile managers (stow/chezmoi/mackup) symlink ~/.agent-reach or its config.yaml","After any home-directory tooling change, run `readlink -f ~/.agent-reach/config.yaml` and confirm the path is real","Sync configs across machines by copying (rsync/scp), not by linking"],"tags":["config","security","symlink","yaml","write-protection"],"backgroundTag":null,"analyzedSha":"93ae1d18c37b707dec053c7c4f9d91cd8ef8943d","analyzedAt":"2026-08-14T22:54:06.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}