T09 · Insecure Skill Coding Practices
- Location
scripts/collect_summaries_bundle.py:111- Finding
Unrestricted Local File Read Through User-Controlled Path Arguments
- Content
View full analysis
Vulnerability Details
File Location:
scripts/collect_summaries_bundle.py:25-33, 111-116;scripts/render_collection_report.py:35-47, 146-150, 189-195
Vulnerability Type: Path traversal and arbitrary local file read
Risk Level: MediumVulnerable Code
python # scripts/collect_summaries_bundle.py parser.add_argument( "--summary-file-name", default="summary.md", help="Per-paper summary file name.", )python # scripts/collect_summaries_bundle.py summary_path = child / summary_file_name if not meta_json.exists() and not meta_md.exists(): continue metadata = read_json(meta_json) if meta_json.exists() else {} if not metadata: metadata = parse_metadata_md(meta_md) summary_text = summary_path.read_text() if summary_path.exists() else ""python # scripts/render_collection_report.py parser.add_argument( "--template-file", default="collection_report_template.md", help="Template markdown file name or path. Default: collection_report_template.md", ) parser.add_argument( "--summary-file-name", default="summary.md", help="Per-paper summary file name. Default: summary.md", )python # scripts/render_collection_report.py def resolve_template_path(base_dir: Path, template_file: str) -> Path: candidate = Path(template_file).expanduser() if candidate.is_absolute(): return candidate return base_dir / candidatepython # scripts/render_collection_report.py template_path = resolve_template_path(base_dir, args.template_file) if not template_path.exists(): print(f"[ERROR] template file not found: {template_path}") return 1 lang_code = normalize_language(args.language) template_text = template_path.read_text()python # scripts/render_collection_report.py summary_path = paper_dir / args.summary_file_name if not summary_path.exists(): unresolved.append({"line": str(line_no), "arxiv_id": maybe_id, "reason": f"missing {args.summary_file_name}"}) o ...[truncated 2031 chars]- Remediation
View remediation
Remediation Suggestions
- Restrict
--summary-file-nameto a plain basename by rejecting absolute paths, path separators,.and..components. - Resolve every input path and verify that it remains under its authorized root before reading:
python def confined_path(root: Path, value: str) -> Path: root = root.resolve() candidate = (root / value).resolve() try: candidate.relative_to(root) except ValueError as exc: raise ValueError(f"Path escapes authorized directory: {value}") from exc return candidate- Use the individual paper directory as the authorized root for summary files.
- Use
base_diras the authorized root for templates unless external templates are an explicit requirement. - If external templates are required, place that behavior behind a clearly documented opt-in flag and validate the approved external directory.
- Validate that input paths are regular files rather than directories, devices, or unexpected symbolic-link targets.
- Return an error before reading any path that fails containment validation.
- Restrict
