T09 · Insecure Skill Coding Practices
- Location
scripts/generate.py:130- Finding
Unrestricted Prompt File Read Can Disclose Local Files to OpenAI
- Content
View full analysis
Vulnerability Details
File Location:
scripts/generate.py:85-97, 130-131
Vulnerability Type: Arbitrary local file disclosure through an unrestricted CLI path
Risk Level: MediumVulnerable Code
python if reference_images: files = [] assets_root = assets_dir.resolve() for fname in reference_images: candidate = Path(fname) if candidate.is_absolute() or ".." in candidate.parts or candidate.name != fname: raise ValueError( f"Invalid reference image {fname!r}: must be a bare filename inside assets_dir." ) p = (assets_dir / fname).resolve() if assets_root not in p.parents and p != assets_root: raise ValueError( f"Reference image {fname!r} resolves outside assets_dir ({assets_root})." ) if not p.exists(): raise FileNotFoundError(f"Reference image not found: {p}") files.append(open(p, "rb")) try: result = client.images.edit( model=model, image=files, prompt=prompt, size=api_size, quality=quality, )python else: result = client.images.generate( model=model, prompt=prompt, size=api_size, quality=quality, )python prompt = Path(args.prompt_file).read_text() render( prompt=prompt, out_name=args.out_name, size=args.size, reference_images=args.reference_images or None, out_dir=args.out_dir, assets_dir=args.assets_dir, model=args.model, quality=args.quality, )Technical Analysis
The
prompt_fileCLI argument is converted directly into aPathand read without validating where it resolves. The implementation does not reject absolute paths, parent-directory traversal, or symbolic links that resolve outside the intended in ...[truncated 1816 chars]- Remediation
View remediation
Remediation Suggestions
- Define a trusted prompt root, such as
infographics/prompts, and resolve both the root and requested file before reading. - Reject absolute paths,
..components, non-regular files, and resolved paths outside the trusted root. - Restrict accepted extensions to the formats the workflow requires, such as
.txtor.md. - Explicitly address symbolic links by rejecting them or verifying that the final resolved target remains under the trusted root.
- Prefer receiving prompt content through a controlled API parameter or standard input rather than accepting an arbitrary filesystem path.
- Apply a reasonable prompt size limit before reading or transmitting the file.
- Preserve the existing user warning as defense in depth, but do not rely on documentation as the primary control.
Example hardening pattern:
python prompts_root = Path("infographics/prompts").resolve() candidate = Path(args.prompt_file) if candidate.is_absolute() or ".." in candidate.parts: raise ValueError("Prompt file must be inside infographics/prompts") prompt_path = (prompts_root / candidate).resolve() if prompts_root not in prompt_path.parents: raise ValueError("Prompt file resolves outside the permitted directory") if not prompt_path.is_file() or prompt_path.suffix not in {".txt", ".md"}: raise ValueError("Prompt file must be a regular .txt or .md file") prompt = prompt_path.read_text(encoding="utf-8")- Define a trusted prompt root, such as
