{"record":{"id":"df8d97c839291e1b","repo":"HKUDS/Vibe-Trading","slug":"invalid-swarm-agent-id-agent-id-r-artifact-path","errorCode":null,"errorMessage":"Invalid swarm agent id {agent_id!r}: artifact path escapes the run artifacts directory","messagePattern":"Invalid swarm agent id (.+?): artifact path escapes the run artifacts directory","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/swarm/worker.py","lineNumber":379,"sourceCode":"    artifact_root = run_dir / \"artifacts\"\n    if (\n        not isinstance(agent_id, str)\n        or not agent_id\n        or agent_id in {\".\", \"..\"}\n        or \"/\" in agent_id\n        or \"\\\\\" in agent_id\n    ):\n        raise ValueError(\n            f\"Invalid swarm agent id {agent_id!r}: expected one safe path segment\"\n        )\n\n    artifact_dir = artifact_root / agent_id\n    resolved_root = artifact_root.resolve()\n    resolved_dir = artifact_dir.resolve()\n    try:\n        relative = resolved_dir.relative_to(resolved_root)\n    except ValueError as exc:\n        raise ValueError(\n            f\"Invalid swarm agent id {agent_id!r}: artifact path escapes \"\n            \"the run artifacts directory\"\n        ) from exc\n    if len(relative.parts) != 1:\n        raise ValueError(\n            f\"Invalid swarm agent id {agent_id!r}: artifact path must be \"\n            \"one level below the run artifacts directory\"\n        )\n    return artifact_dir\n\n\ndef clear_agent_artifacts(artifact_dir: Path) -> None:\n    \"\"\"Remove *artifact_dir* and everything in it, before a retry attempt.\n\n    A retry re-invokes :func:`run_worker` against the same ``artifact_dir``.\n    Without this, a failed attempt's ``report.md`` (or any other file a tool\n    wrote) would still be sitting there when the retried attempt reads the\n    directory back via ``_resolve_summary``/``_report_written``/","sourceCodeStart":361,"sourceCodeEnd":397,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/swarm/worker.py#L361-L397","documentation":"Raised by agent_artifact_dir in agent/src/swarm/worker.py when the resolved artifact directory for a swarm agent id does not lie inside the resolved run artifacts root. It is a path-traversal guard: agent ids like '../foo', '/etc', or symlinked paths that canonicalize outside the root are rejected.","triggerScenarios":"Calling agent_artifact_dir (directly or via _run_worker_with_retries/_run_worker_impl) with an agent_id containing '/', '..', leading '/', or an id that resolves through a symlink to a location outside artifact_root.resolve().","commonSituations":"Passing an LLM-generated or user-supplied agent id unchecked into the swarm worker; agent ids built from file paths; a symlink placed inside the artifacts directory escaping the run dir.","solutions":["Sanitize/validate agent ids to a simple identifier charset (e.g. r'^[A-Za-z0-9_-]+$') before calling the swarm worker","If the id legitimately contains separators, derive a safe slug or hash instead of using the raw string","Remove or relocate symlinks inside the artifacts root so resolution stays contained"],"exampleFix":"// before\nartifact_dir = agent_artifact_dir(root, agent_id)  # agent_id = '../evil'\n// after\nimport re\nif not re.fullmatch(r'[A-Za-z0-9_.-]+', agent_id):\n    raise ValueError(f'bad agent id: {agent_id!r}')\nartifact_dir = agent_artifact_dir(root, agent_id)","handlingStrategy":"validation","validationCode":"import re\nSAFE_ID = re.compile(r'^[A-Za-z0-9_-]+$')\nassert SAFE_ID.fullmatch(agent_id), f'unsafe agent id: {agent_id!r}'","typeGuard":"def is_safe_agent_id(agent_id: str) -> bool:\n    return bool(re.fullmatch(r'[A-Za-z0-9_-]+', agent_id)) and '..' not in agent_id","tryCatchPattern":"try:\n    d = agent_artifact_dir(root, agent_id)\nexcept ValueError as e:\n    log.warning('rejecting agent id %r: %s', agent_id, e)\n    agent_id = hashlib.sha256(agent_id.encode()).hexdigest()[:16]\n    d = agent_artifact_dir(root, agent_id)","preventionTips":["Never pass raw LLM/user strings as filesystem path components","Normalize agent ids to a fixed charset at ingestion time","Keep the artifacts root free of symlinks you don't control"],"tags":["security","path-traversal","swarm","validation"],"backgroundTag":"path-traversal-escape","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}