T09 · Insecure Skill Coding Practices
- Location
scripts/doc_fetch.py:20- Finding
Arbitrary Local File Read Through Untrusted Index Paths in Document Fetcher
- Content
View full analysis
Vulnerability Details
File Location:
scripts/doc_fetch.py, lines 20-22, 39-52, 176, and 197-211
Vulnerability Type: Arbitrary local file read caused by trusting paths from an untrusted index
Risk Level: MediumVulnerable Code
python def load_index(index_path: str) -> Dict[str, Any]: """加载索引文件""" with open(index_path, 'r', encoding='utf-8') as f: return json.load(f)python def find_file_in_index(index: Dict[str, Any], filename: str) -> Optional[Dict[str, Any]]: """在索引中查找文件""" files = index.get('files', []) # 精确匹配 for f in files: if f['filename'] == filename or f['filepath'] == filename: return f # 模糊匹配 for f in files: if filename.lower() in f['filename'].lower() or filename.lower() in f['filepath'].lower(): return f return Nonepython filepath = file_info['filepath']python if args.lines: start, end = parse_line_range(args.lines) print(f'📍 行范围: {start}-{end}') print('-' * 60) print(fetch_lines(filepath, start, end)) return 0 # 获取全部 if args.all: print(f'📄 全文内容 (最多 {args.max_lines} 行):') print('-' * 60) print(fetch_all(filepath, args.max_lines)) return 0The resulting path reaches direct file operations in
fetch_lines()andfetch_all():python with open(filepath, 'r', encoding='utf-8', errors='ignore') as f: lines = f.readlines()Technical Analysis
The utility treats the index JSON as authoritative and directly uses its
filepathfield when reading documents. It does not canonicalize the path, enforce a trusted document root, verify that the path belongs to the indexed source directory, reject symlink escapes, or compare the current file against the stored hash.Consequently, anyone able to provide or modify an index file can point an entry at any file readable by the process. Selecting that entry with
--file, or relying on automatic selection when ...[truncated 1212 chars]- Remediation
View remediation
Remediation Suggestions
- Require a trusted document root when loading an index.
- Store indexed file paths relative to that root rather than as unrestricted absolute paths.
- Resolve both the trusted root and candidate path with
Path.resolve(), then verify that the candidate remains beneath the root:python root = Path(document_root).resolve() candidate = (root / file_info["filepath"]).resolve() try: candidate.relative_to(root) except ValueError: raise ValueError("Indexed path escapes the trusted document root") - Define and validate a strict index schema before accessing fields.
- Reject absolute paths and parent-directory traversal components in imported index entries.
- Consider rejecting symlinks or explicitly checking every resolved component when symlink traversal is not required.
- Verify the current file hash against the hash stored in the index before reading.
- Avoid accepting indexes from untrusted sources. If imported indexes are required, document that they are untrusted and require explicit confirmation before accessing paths outside the expected source tree.
- Add regression tests covering absolute paths,
../traversal, symlink escapes, malformed index entries, and hash mismatches.
