T09 · Insecure Skill Coding Practices
- Location
scripts/fetch_month.py:174- Finding
Predictable Temporary File Permits Symlink-Based Overwrite and Local Data Exposure
- Content
View full analysis
Vulnerability Details
File Location:
scripts/fetch_month.py, lines 174–176
Vulnerability Type: Predictable and insecure temporary-file handling
Risk Level: Mediumpython output_file = f"/tmp/flomo_{args.year}_{args.month:02d}.json" with open(output_file, 'w', encoding='utf-8') as f: json.dump(result['memos'], f, ensure_ascii=False, indent=2)Technical Analysis
The script stores complete Flomo note contents at a predictable path in the shared
/tmpdirectory. Python's ordinaryopen(..., 'w')follows symbolic links and truncates an existing target. It does not provide exclusive creation, symbolic-link rejection, or an explicit restrictive permission mode.Because the year and month determine the filename, another local user can predict it before execution and create a symbolic link at that path. When the victim runs the script, the process follows the link and writes using the victim's permissions.
The resulting archive permissions also depend on the process umask. Under a permissive configuration, private note contents may become readable by other local users.
Attack Path
- An attacker with local access predicts the output path, such as
/tmp/flomo_2025_07.json. - Before the victim runs the skill, the attacker creates that path as a symbolic link to a file writable by the victim.
- The victim invokes
fetch_month.pyfor the corresponding year and month. - The script opens the predictable path with truncation enabled and follows the symbolic link.
- The linked target is truncated and replaced with serialized Flomo note data.
- If resulting permissions are insufficiently restrictive, the attacker may also read the archived private notes.
The attacker cannot use this issue to overwrite files the victim process lacks permission to modify.
Impact Assessment
Successful exploitation can compromise:
- Integrity and availability: Arbitrary files writable by the victim may be truncated and replaced.
- **C ...[truncated 406 chars]
- An attacker with local access predicts the output path, such as
- Remediation
View remediation
Remediation Suggestions
- Store archives in a user-private directory rather than shared
/tmp, with the directory mode restricted to0700. - Create output files atomically and exclusively with mode
0600. - Reject symbolic links by using
os.open()withO_CREAT | O_EXCL | O_WRONLYandO_NOFOLLOWwhere supported. - Alternatively, use Python's
tempfilefacilities to generate an unpredictable, securely created file. - If the final filename must remain stable, securely create a temporary file in the same private directory, flush and close it, and then atomically rename it.
- Refuse to overwrite an existing output path unless the user explicitly requests replacement and the path has been validated as a regular file owned by the current user.
- Store archives in a user-private directory rather than shared
