{"record":{"id":"199d5badd1102bc5","repo":"agentscope-ai/agentscope","slug":"workspace-id-workspace-id-r-escapes-the-workspac","errorCode":null,"errorMessage":"workspace_id {workspace_id!r} escapes the workspace base directory","messagePattern":"workspace_id (.+?) escapes the workspace base directory","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/agentscope/app/workspace_manager/_docker_workspace_manager.py","lineNumber":184,"sourceCode":"        filesystem. Anything not landing strictly inside is rejected.\n\n        Args:\n            workspace_id (`str`):\n                The workspace whose bind mount is being resolved.\n            user_id (`str`, defaults to `\"\"`):\n                Owner of the workspace, for the legacy layout below.\n                Empty for a workspace nobody owns yet.\n            agent_id (`str`, defaults to `\"\"`):\n                Agent of the workspace, for the legacy layout below.\n\n        Raises:\n            `ValueError`:\n                If ``workspace_id`` resolves outside ``basedir``.\n        \"\"\"\n        root = os.path.realpath(self._basedir)\n        workdir = os.path.realpath(os.path.join(root, workspace_id))\n        if not workdir.startswith(root + os.sep):\n            raise ValueError(\n                f\"workspace_id {workspace_id!r} escapes the workspace \"\n                f\"base directory\",\n            )\n        if os.path.isdir(workdir) or not (user_id and agent_id):\n            return workdir\n\n        # Workspaces built before the id-keyed layout live under\n        # ``<basedir>/<user_id>/<agent_id>``. Keep mounting such a\n        # directory where it stands: several workspace ids may share\n        # one, so no rename can move them all. Legacy paths escaping\n        # ``basedir`` are declined rather than rejected, leaving the\n        # caller with an ordinary empty workspace.\n        legacy = os.path.realpath(os.path.join(root, user_id, agent_id))\n        if legacy.startswith(root + os.sep) and os.path.isdir(legacy):\n            return legacy\n        return workdir\n\n    # ── workspace construction ────────────────────────────────────","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/agentscope-ai/agentscope/blob/e90f1c7592896cc95f6e5ee506194f533378247d/src/agentscope/app/workspace_manager/_docker_workspace_manager.py#L166-L202","documentation":"DockerWorkspaceManager._workdir_for computes realpath(basedir/workspace_id) and rejects ids whose resolved path falls outside basedir, guarding against '../' traversal in workspace_id.","triggerScenarios":"Passing a workspace_id containing '..' segments, absolute-path-like content, or values whose realpath (via pre-existing symlinks) escapes basedir during _build_and_start.","commonSituations":"User-supplied or LLM-generated workspace ids containing traversal sequences; symlinked basedir directories; migrating workspace ids between layouts with prefix '../'.","solutions":["Normalize/validate workspace_id to a single safe path component (alnum, dash, underscore) before starting a workspace","Use generated uuids as workspace ids rather than free-form strings","Use realpath(basedir) when constructing the manager to avoid symlink-induced escapes"],"exampleFix":"# before\nawait mgr._build_and_start(workspace_id=\"../../etc\", ...)\n# after\nimport re\nws = re.sub(r\"[^A-Za-z0-9_-]\", \"-\", ws)\nawait mgr._build_and_start(workspace_id=ws, ...)","handlingStrategy":"validation","validationCode":"import re\nassert re.fullmatch(r\\\"[A-Za-z0-9_-]+\\\", workspace_id), \\\"unsafe workspace id\\\"","typeGuard":"def is_safe_workspace_id(ws_id: str) -> bool:\\n    import re\\n    return bool(re.fullmatch(r\\\"[A-Za-z0-9_.-]+\\\", ws_id)) and \\\"..\\\" not in ws_id","tryCatchPattern":"try:\\n    await mgr._build_and_start(workspace_id=ws, ...)\\nexcept ValueError as e:\\n    if \\\"escapes\\\" in str(e): ws = sanitize(ws); retry\\n    else: raise","preventionTips":["Generate workspace ids as uuids","Never build ids from user free text"],"tags":["path-traversal","security","docker","workspace"],"backgroundTag":"path-traversal-blocked","analyzedSha":"e90f1c7592896cc95f6e5ee506194f533378247d","analyzedAt":"2026-08-28T18:24:12.087Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}