{"record":{"id":"fc4752830565ca18","repo":"HKUDS/Vibe-Trading","slug":"artifact-path-escapes-the-current-run-dir","errorCode":null,"errorMessage":"artifact_path escapes the current run_dir","messagePattern":"artifact_path escapes the current run_dir","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/tools/goal_tool.py","lineNumber":93,"sourceCode":"    artifact_path = str(kwargs.get(\"artifact_path\") or \"\").strip() or None\n    artifact_hash = str(kwargs.get(\"artifact_hash\") or \"\").strip() or None\n\n    run_dir_raw = str(kwargs.get(\"run_dir\") or \"\").strip()\n    run_dir: Path | None = None\n    if run_dir_raw:\n        run_dir = safe_run_dir(run_dir_raw)\n        if run_id is None:\n            run_id = run_dir.name\n\n    artifact_candidate: Path | None = None\n    if artifact_path:\n        raw_path = Path(artifact_path).expanduser()\n        if run_dir is not None and not raw_path.is_absolute():\n            resolved = (run_dir / raw_path).resolve()\n            try:\n                resolved.relative_to(run_dir)\n            except ValueError as exc:\n                raise ValueError(\"artifact_path escapes the current run_dir\") from exc\n            artifact_candidate = resolved\n            artifact_path = str(resolved)\n        elif raw_path.is_absolute():\n            artifact_candidate = raw_path.resolve()\n\n    if artifact_candidate is not None and artifact_candidate.is_file() and not artifact_hash:\n        artifact_hash = _sha256_file(artifact_candidate)\n\n    return run_id, artifact_path, artifact_hash\n\n\nclass _GoalToolBase(BaseTool):\n    \"\"\"Shared helpers for local goal tools.\"\"\"\n\n    repeatable = True\n\n    def __init__(\n        self,","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/tools/goal_tool.py#L75-L111","documentation":"When a relative artifact_path is supplied with a run_dir, the tool resolves run_dir/path and requires it to stay inside run_dir; '../' sequences that escape raise this. It is a path-traversal guard ensuring evidence files belong to the current run.","triggerScenarios":"artifact_path='../../etc/passwd', 'logs/../../../secrets.txt', or any relative path whose resolved location leaves run_dir. Also symlinked run_dir contents pointing outside after resolve().","commonSituations":"LLM-generated artifact paths with ../; joined paths from inconsistent working directories; attempts to reference artifacts from prior runs stored elsewhere.","solutions":["Use a path relative to run_dir without '../' segments","Use an absolute path to the artifact (absolute paths take the elif branch and are allowed if the file exists)","Store shared artifacts inside run_dir or pass explicit artifact_hash instead of a path"],"exampleFix":"# before\nexecute(artifact_path=\"../run-42/report.md\")\n# after\nexecute(artifact_path=\"run-42/report.md\", ...)  # or absolute path","handlingStrategy":"validation","validationCode":"from pathlib import Path\np = Path(artifact_path)\nif not p.is_absolute():\n    assert \"..\" not in p.parts, f\"artifact_path must stay inside run_dir: {artifact_path}\"","typeGuard":"def path_is_within(path_str: str, run_dir) -> bool:\n    from pathlib import Path\n    p = Path(path_str)\n    if p.is_absolute():\n        return True\n    try:\n        (run_dir / p).resolve().relative_to(run_dir)\n        return True\n    except ValueError:\n        return False","tryCatchPattern":"try:\n    execute(artifact_path=artifact_path, ...)\nexcept ValueError as e:\n    if \"escapes the current run_dir\" in str(e):\n        artifact_path = str((run_dir / Path(artifact_path).name).resolve())\n        execute(artifact_path=artifact_path, ...)\n    raise","preventionTips":["Never generate '../' in artifact paths","Prefer absolute paths for out-of-run evidence","Pass artifact_hash when the file lives outside run_dir"],"tags":["python","security","path-traversal","validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}