T09 · Insecure Skill Coding Practices
- Location
scripts/generate_image_doc.py:63- Finding
Path Traversal Enables Unauthorized Local Image Disclosure
- Content
View full analysis
Vulnerability Details
File Location:
scripts/generate_image_doc.py:63-67andscripts/generate_image_doc.py:190-196
Vulnerability Type: Path traversal and arbitrary local file read
Risk Level: MediumVulnerable Code
python page_path = os.path.join(pages_dir, source_page) if not os.path.exists(page_path): raise FileNotFoundError(f"整页渲染图不存在: {page_path}") img = Image.open(page_path)The independent-image processing path contains the same issue:
python elif image_file: # 使用独立图片文件 image_path = os.path.join(images_dir, image_file) if not image_path or not os.path.exists(image_path): skip_para = doc.add_paragraph()The selected file is subsequently opened and inserted into the generated document:
python with PILImage.open(image_path) as img: img_width, img_height = img.size max_width_cm = 14 aspect = img_height / img_width display_width = min(max_width_cm, img_width * 2.54 / 96) img_para = doc.add_paragraph() img_para.alignment = WD_ALIGN_PARAGRAPH.CENTER img_run = img_para.add_run() img_run.add_picture(image_path, width=Cm(display_width))Technical Analysis
The
source_pageandimage_filevalues originate from the user-supplied mapping JSON. They are joined directly withpages_dirorimages_dirwithout rejecting absolute paths, resolving traversal components, or verifying that the resulting path remains inside the intended directory.For example, a mapping entry can use:
json { "image_file": "../../private/sensitive-image.png", "question_number": 1, "page": 1, "context": "Question illustration" }Because
os.path.join()does not enforce directory confinement,..components can escape the configured image directory. An absoluteimage_fileorsource_pagecan also cause the base directory to be discarded entirely. If the target is a supported image readable by the process, the script opens it and embeds it in the generated DOCX.The issue affects both support ...[truncated 1300 chars]
- Remediation
View remediation
Remediation Suggestions
Resolve and validate every mapping-provided path before opening it:
python from pathlib import Path def confined_file(base_dir: str, untrusted_name: str) -> Path: base = Path(base_dir).resolve() candidate_input = Path(untrusted_name) if candidate_input.is_absolute(): raise ValueError("Absolute image paths are not permitted") candidate = (base / candidate_input).resolve() try: candidate.relative_to(base) except ValueError: raise ValueError("Image path escapes the configured directory") if not candidate.is_file(): raise FileNotFoundError(f"Image file does not exist: {candidate}") return candidateApply this validation to both paths:
python page_path = confined_file(pages_dir, source_page) image_path = confined_file(images_dir, image_file)Additional hardening measures should include:
- Permit only expected filename forms, such as
image_12.pngandpage_3.png, where practical. - Allowlist supported image extensions and verify the actual decoded image format.
- Reject null bytes, absolute paths, traversal components, and symbolic links that resolve outside the base directory.
- Validate the mapping JSON against a strict schema before processing it.
- Run document generation with minimal filesystem permissions.
- Treat mapping files from untrusted sources as hostile input and avoid sharing generated documents until their embedded content has been reviewed.
- Permit only expected filename forms, such as
