T05 · Unauthorized Access and Privilege Escalation
Warning
- Location
- scripts/generate_report.py:693
- Finding
- Arbitrary Local Text File Disclosure Through the Market News Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_report.py:693-704` **Vulnerability Type**: Unrestricted local file read **Risk Level**: Medium ### Vulnerable Code ```python excel_file = sys.argv[1] news_file = sys.argv[2] if len(sys.argv) > 2 else None # Read Excel data excel_data = read_fund_excel(excel_file) # Read market news if provided market_news = [] if news_file: with open(news_file, 'r', encoding='utf-8') as f: market_news = [line.strip() for line in f if line.strip()] # Generate report output_path = generate_weekly_report(excel_data, market_news) print(f"✅ Report generated: {output_path}") ``` The resulting data is incorporated into the document: ```python if market_news: generate_market_news_section(doc, market_news) ``` ### Technical Analysis The optional `news_file` command-line argument is passed directly to Python's `open()` function. The code performs no validation of: - The file extension or actual file type. - Whether the file is inside an authorized workspace. - Whether the path references a sensitive system directory. - Whether the path is a symbolic link to a sensitive file. - The size of the file being read. This behavior conflicts with the security boundary declared in `SKILL.md`, which states that the Skill only reads explicitly provided Excel files, does not read non-Excel files, and does not access sensitive directories. Because every nonempty line is placed into `market_news` and subsequently included in the generated Word report, this is not merely an incidental read. It creates a practical local data-disclosure channel. ### Attack Path 1. The attacker supplies a valid fund workbook as the first argument. 2. The attacker supplies the path of a readable local text file as the second argument, such as a configuration, log, environment, or credential file. 3. `open(news_file, 'r', encoding='utf-8')` reads the file under the privileges of the Agent process. 4. Every nonempty line is s ...[truncated 990 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove the market-news file feature if it is not required by the declared Skill functionality. 2. If the feature is required, explicitly document it and restrict it to user-provided files in an approved upload directory. 3. Canonicalize the path before use: ```python from pathlib import Path ALLOWED_INPUT_ROOT = Path("/approved/workspace/uploads").resolve() def validate_news_file(file_path: str) -> Path: candidate = Path(file_path) if candidate.is_symlink(): raise ValueError("Symbolic links are not permitted") resolved = candidate.resolve(strict=True) if resolved.suffix.lower() not in {".txt", ".md"}: raise ValueError("Only approved text file types are permitted") if resolved != ALLOWED_INPUT_ROOT and ALLOWED_INPUT_ROOT not in resolved.parents: raise ValueError("File is outside the approved input directory") if resolved.stat().st_size > 1_000_000: raise ValueError("Input file exceeds the size limit") return resolved ``` 4. Reject sensitive directories and symbolic links as defense-in-depth, but do not rely on a substring denylist as the primary boundary. 5. Apply a strict size limit and handle encoding errors safely. 6. Ensure that sensitive local content cannot be inserted into generated reports without explicit user authorization. ]]>
