{"record":{"id":"6ff2be6e3bb59a27","repo":"shareAI-lab/learn-claude-code","slug":"mailbox-path-escapes-directory-agent-r-6ff2be","errorCode":null,"errorMessage":"Mailbox path escapes directory: {agent!r}","messagePattern":"Mailbox path escapes directory: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"s15_integrated_harness/code.py","lineNumber":1027,"sourceCode":"VALID_AGENT_NAME = re.compile(r\"^[A-Za-z0-9_-]{1,64}$\")\nRESERVED_TEAMMATE_NAMES = {\"lead\", \"agent\"}\n\n\ndef is_valid_agent_name(name: str) -> bool:\n    return bool(VALID_AGENT_NAME.fullmatch(name))\n\n\nclass MessageBus:\n    def __init__(self):\n        self._lock = threading.RLock()\n        self._changed = threading.Condition(self._lock)\n\n    def _path(self, agent: str) -> Path:\n        if not is_valid_agent_name(agent):\n            raise ValueError(f\"Invalid mailbox recipient: {agent!r}\")\n        path = (MAILBOX_DIR / f\"{agent}.jsonl\").resolve()\n        if not path.is_relative_to(MAILBOX_ROOT):\n            raise ValueError(f\"Mailbox path escapes directory: {agent!r}\")\n        return path\n\n    def _read_unlocked(self, agent: str) -> list[dict]:\n        inbox = self._path(agent)\n        if not inbox.exists():\n            return []\n        msgs = [json.loads(line) for line in inbox.read_text().splitlines()\n                if line.strip()]\n        inbox.unlink()\n        return msgs\n\n    def send(self, from_agent: str, to_agent: str, content: str,\n             msg_type: str = \"message\", metadata: dict | None = None):\n        msg = {\"from\": from_agent, \"to\": to_agent,\n               \"content\": content, \"type\": msg_type,\n               \"ts\": time.time(), \"metadata\": metadata or {}}\n        with self._changed:\n            MAILBOX_DIR.mkdir(parents=True, exist_ok=True)","sourceCodeStart":1009,"sourceCodeEnd":1045,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s15_integrated_harness/code.py#L1009-L1045","documentation":"The second guard in MessageBus._path(): even with a lexically valid name, if MAILBOX_DIR/<agent>.jsonl resolves outside MAILBOX_ROOT the path is refused. This catches symlinked mailbox files or a relocated/symlinked MAILBOX_DIR that would let messages escape the mailbox store — a path-traversal defense in depth after the regex check.","triggerScenarios":"A pre-created <agent>.jsonl symlink inside MAILBOX_DIR pointing elsewhere; MAILBOX_DIR itself a symlink outside the workspace root; MAILBOX_ROOT not under WORKDIR after config changes.","commonSituations":"Users symlinking a mailbox into a shared dir; packaging the harness with the mailbox dir on an external mount; leftover symlinks from debugging.","solutions":["Remove symlinks from the mailbox directory; mailboxes are plain .jsonl files managed by MessageBus.","Keep MAILBOX_DIR/MAILBOX_ROOT as real directories inside the workspace.","Audit with `find <mailbox_dir> -type l` and delete offenders."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef mailbox_store_ok(mailbox_dir: Path, mailbox_root: Path) -> bool:\n    resolved = mailbox_dir.resolve()\n    return resolved.is_relative_to(mailbox_root.resolve()) and not any(\n        p.is_symlink() for p in resolved.glob(\"*\") if p.is_file() or p.is_symlink()\n    )","typeGuard":null,"tryCatchPattern":"try:\n    bus.send(a, b, msg)\nexcept ValueError as e:\n    if \"escapes directory\" in str(e):\n        raise SystemExit(\"mailbox directory misconfigured (symlink/escape)\")\n    raise","preventionTips":["Never place symlinks in the mailbox directory.","Keep MAILBOX_DIR a real directory under the workspace.","Audit with `find <mailbox_dir> -type l` after setup changes."],"tags":["security","path-traversal","symlink","message-bus"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}