T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/analyze_journal.py:463
- Finding
- Untrusted Journal Title Enables Output Path Traversal<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze_journal.py`, lines 463–474 **Vulnerability Type**: Path traversal and unintended file overwrite **Risk Level**: Medium ### Vulnerable Code ```python journal_name = data.get('journal_info', {}).get('title', '未知期刊') articles = data.get('articles', []) print(f"期刊: {journal_name}") print(f"文章总数: {len(articles)}") chart_dir = os.path.join(output_dir, f'{journal_name}_charts') charts = create_charts(articles, chart_dir, journal_name) print(f"图表已生成: {chart_dir}/") report_path = os.path.join(output_dir, f'{journal_name}_近五年发文分析报告.docx') generate_report(data, charts, report_path) print(f"报告已生成: {report_path}") ``` The resulting paths are used by the following file-writing operations: ```python os.makedirs(output_dir, exist_ok=True) ``` ```python doc.save(output_path) ``` ### Technical Analysis The journal title is read directly from the user-supplied JSON document and incorporated into filesystem paths without validation or normalization. Python's `os.path.join()` does not guarantee that the result remains beneath the intended output directory. A title containing path separators, traversal components such as `../`, or an absolute path can cause `chart_dir` and `report_path` to resolve outside `output_dir`. The program subsequently creates the chart directory, writes fixed-name PNG files into it, and saves the generated Word document at the derived report path. The report filename has a fixed suffix, so an attacker cannot select every possible destination filename. However, the attacker can still control the parent path and filename prefix. The chart directory similarly receives a fixed suffix, but generated files within that directory can overwrite existing files with names such as `01_yearly_trend.png`. ### Attack Path 1. An attacker prepares an otherwise valid input JSON file. 2. The attacker assigns a traversal or absolute-path value to `journal_info.title`, for example a value begin ...[truncated 978 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Treat `journal_info.title` as display-only data and never use it directly as a filesystem component. 2. Generate a safe filename using a strict allowlist. Remove path separators, control characters, traversal sequences, and platform-specific reserved characters. 3. Reject absolute paths and titles whose sanitized form is empty. 4. Resolve and verify every destination before writing: ```python import re from pathlib import Path def safe_filename_component(value): value = re.sub(r'[^\w.-]+', '_', str(value), flags=re.UNICODE) value = value.strip(' ._') if not value or value in {'.', '..'}: raise ValueError("Invalid journal title") return value base_dir = Path(output_dir).expanduser().resolve() base_dir.mkdir(parents=True, exist_ok=True) safe_name = safe_filename_component(journal_name) chart_dir = (base_dir / f"{safe_name}_charts").resolve() report_path = (base_dir / f"{safe_name}_five_year_analysis.docx").resolve() if base_dir not in chart_dir.parents: raise ValueError("Chart path escapes the output directory") if base_dir not in report_path.parents: raise ValueError("Report path escapes the output directory") ``` 5. Refuse to overwrite existing output files by default, or require an explicit overwrite option. 6. Where output directories may be attacker-controlled, guard against symbolic-link redirection and use safe file-creation semantics. ]]>
