{"record":{"id":"587367a107f633fc","repo":"nexu-io/open-design","slug":"path-traversal-detected-in-output-path-for-job-jo","errorCode":null,"errorMessage":"path traversal detected in output_path for job {job_id}","messagePattern":"path traversal detected in output_path for job (.+?)","errorType":"exception","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"skills/hatch-pet/scripts/generate_pet_images.py","lineNumber":263,"sourceCode":"        states=parse_states(args.states),\n        skip_base=args.skip_base,\n        job_ids=args.job_id,\n    )\n    raw_dir = run_dir / \"raw\"\n\n    completed = []\n    for job in jobs:\n        job_id = str(job.get(\"id\"))\n        prompt_raw = job.get(\"prompt_file\")\n        output_raw = job.get(\"output_path\")\n        if not isinstance(prompt_raw, str) or not isinstance(output_raw, str):\n            raise SystemExit(f\"job {job_id} is missing prompt_file or output_path\")\n        prompt_file = (run_dir / prompt_raw).resolve()\n        output_image = (run_dir / output_raw).resolve()\n        if not prompt_file.is_relative_to(run_dir):\n            raise SystemExit(f\"path traversal detected in prompt_file for job {job_id}\")\n        if not output_image.is_relative_to(run_dir):\n            raise SystemExit(f\"path traversal detected in output_path for job {job_id}\")\n        print(f\"Generating {job_id} with secondary fallback\")\n        image_paths = path_list(run_dir, job)\n        if image_paths:\n            response = run_image_edit(\n                model=args.model,\n                prompt_file=prompt_file,\n                image_paths=image_paths,\n                output_json=raw_dir / f\"{job_id}.response.json\",\n                size=args.size,\n                api_key=api_key,\n            )\n        else:\n            response = run_image_generation(\n                model=args.model,\n                prompt_file=prompt_file,\n                output_json=raw_dir / f\"{job_id}.response.json\",\n                size=args.size,\n                api_key=api_key,","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/skills/hatch-pet/scripts/generate_pet_images.py#L245-L281","documentation":"Raised in the main loop after resolving output_path: the resolved destination is not inside run_dir. The script only writes generated images inside the run directory, so an output_path that escapes is rejected.","triggerScenarios":"A job's output_path is absolute or contains ../ that would write the decoded image outside run_dir (e.g. \"/etc/cron.d/evil\" or \"../../../overwrite.png\").","commonSituations":"Hand-editing output_path to point at a shared absolute dir; manifest copied from another machine with absolute output paths; an attempt to overwrite unrelated files via the manifest.","solutions":["Set output_path to a relative path under run_dir, e.g. decoded/<state>.png.","If you need the image elsewhere, write inside run_dir then copy out after the run.","Regenerate the manifest with prepare_pet_run.py.","Rerun the job."],"exampleFix":"// before (manifest)\n\"output_path\": \"/var/www/html/idle.png\"\n\n// after\n\"output_path\": \"decoded/idle.png\"","handlingStrategy":"validation","validationCode":"def safe_output_path(run_dir: Path, raw: str) -> Path:\n    resolved = (run_dir / raw).resolve()\n    if not resolved.is_relative_to(run_dir):\n        raise SystemExit(f\"output_path escapes run_dir: {raw}\")\n    return resolved","typeGuard":"def output_is_inside(run_dir: Path, raw: str) -> bool:\n    return (run_dir / raw).resolve().is_relative_to(run_dir)","tryCatchPattern":null,"preventionTips":["Keep output_path relative to run_dir (e.g. decoded/idle.png).","Copy images out of run_dir after generation if you need them elsewhere; do not redirect output_path.","Regenerate the manifest with prepare_pet_run.py."],"tags":["security","path-traversal","manifest","validation","system-exit"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}