T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/fetch_clawfeed.py:35
- Finding
- Remote Digest Content Can Overwrite Existing Notes<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fetch_clawfeed.py:35-55` **Vulnerability Type**: Untrusted file-content and filename collision handling **Risk Level**: Medium ### Vulnerable Code ```python content = digest["content"] date_match = re.search(r'(\d{4}-\d{2}-\d{2})', content) if date_match: date_str = date_match.group(1) else: date_str = datetime.now().strftime("%Y-%m-%d") if args.type == '4h': filename = f"{date_str}-4h.md" elif args.type == 'weekly': filename = f"周报-{date_str}.md" else: filename = f"{date_str}.md" file_path = os.path.join(ai_news_dir, filename) with open(file_path, "w", encoding="utf-8") as f: f.write(content) ``` ### Technical Analysis The application uses remotely supplied digest content to derive the output date and therefore the destination filename. Although the date regular expression limits the matched value to digits and hyphens and prevents direct path traversal, it does not establish that the date is trusted metadata or even a valid calendar date. The destination is opened in `w` mode. If a file with the generated name already exists, Python truncates and replaces it without confirmation. The filenames are predictable, such as `2026-09-11.md`, so a malicious or compromised upstream service can deliberately select a date that collides with an existing note. ### Attack Path 1. An attacker compromises the ClawFeed API or otherwise causes it to return attacker-controlled digest content. 2. The attacker inserts a date matching `YYYY-MM-DD` that corresponds to an existing note. 3. The script extracts that date and creates the predictable destination filename. 4. The script opens the existing file in `w` mode. 5. The existing note is truncated and replaced with attacker-controlled Markdown. ### Impact Assessment Exploitation does not grant operating-system privileges or arbitrary file-path selection because the filename format is constrained. However, it permits modification ...[truncated 381 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Derive the date from a trusted, structured API field rather than searching the digest body. 2. Parse and validate the value with `datetime.strptime(date_value, "%Y-%m-%d")`. 3. Refuse replacement by default by opening files with exclusive creation mode: ```python with open(file_path, "x", encoding="utf-8") as f: f.write(content) ``` 4. Add an explicit `--overwrite` option if replacement is a required feature. 5. Alternatively, generate unique filenames using a digest identifier or timestamp. 6. Resolve the final path and verify that it remains beneath the intended output directory before writing. 7. Consider writing to a temporary file and using an atomic rename after successful validation. ]]>
