{"record":{"id":"f4afbd9a82579f09","repo":"nexu-io/open-design","slug":"path-traversal-detected-in-prompt-file-for-job-jo","errorCode":null,"errorMessage":"path traversal detected in prompt_file for job {job_id}","messagePattern":"path traversal detected in prompt_file for job (.+?)","errorType":"exception","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"skills/hatch-pet/scripts/generate_pet_images.py","lineNumber":261,"sourceCode":"    jobs = select_jobs(\n        manifest,\n        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\",","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/skills/hatch-pet/scripts/generate_pet_images.py#L243-L279","documentation":"Raised in the main loop after resolving prompt_file: the resolved path is not inside run_dir (is_relative_to(run_dir) is False). Same traversal guard as input_images, applied to the prompt file so a manifest cannot read arbitrary files outside the run.","triggerScenarios":"A job's prompt_file is an absolute path or contains ../ that escapes run_dir (e.g. \"/etc/passwd\" or \"../../secrets/prompt.md\").","commonSituations":"Hand-editing prompt_file to an absolute path for convenience; moving a run dir without updating relative paths; manifest copied across machines with absolute paths baked in.","solutions":["Make prompt_file relative to run_dir (e.g. prompts/rows/idle.md) with no ../ escapes.","Copy the desired prompt into run_dir/prompts and reference it relatively.","Regenerate prompts with prepare_pet_run.py, which writes them under prompts/.","Rerun the job."],"exampleFix":"// before (manifest)\n\"prompt_file\": \"/home/user/prompts/idle.md\"\n\n// after\ncp /home/user/prompts/idle.md /tmp/pet-run/prompts/rows/idle.md\n# manifest:\n\"prompt_file\": \"prompts/rows/idle.md\"","handlingStrategy":"validation","validationCode":"def safe_prompt_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\"prompt_file escapes run_dir: {raw}\")\n    return resolved","typeGuard":"def prompt_is_inside(run_dir: Path, raw: str) -> bool:\n    return (run_dir / raw).resolve().is_relative_to(run_dir)","tryCatchPattern":null,"preventionTips":["Keep prompt_file relative to run_dir (e.g. prompts/rows/idle.md).","Regenerate prompts via prepare_pet_run.py rather than editing the manifest.","Reject manifests produced on another machine that bake in absolute paths."],"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"}