T09 · Insecure Skill Coding Practices
- Location
main.py:28- Finding
Fixed State Path Allows Symlink-Based File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
main.py, lines 12 and 28-30
Vulnerability Type: Symlink following and unsafe state-file handling
Risk Level: MediumVulnerable Code
python class KnowledgeKeeper: def __init__(self): self.notes_file = "knowledge.json" self.load_notes() def load_notes(self): """Load notes from file""" if os.path.exists(self.notes_file): with open(self.notes_file, 'r', encoding='utf-8') as f: self.notes = json.load(f) else: self.notes = { "categories": {}, "tags": {}, "notes": [] } def save_notes(self): """Save notes to file""" with open(self.notes_file, 'w', encoding='utf-8') as f: json.dump(self.notes, f, indent=2, ensure_ascii=False)Technical Analysis
The application stores state at the fixed relative path
knowledge.json. It opens this path directly in write mode without verifying that it is a regular file, checking its ownership, rejecting symbolic links, or securely creating the destination.Python's normal
open(..., 'w')behavior follows symbolic links and truncates the linked target. Consequently, an attacker with write access to the process working directory can place a symbolic link atknowledge.jsonand redirect a subsequent save operation to another path.An existing target generally must contain valid JSON with the expected note structure for initialization and a later mutation to succeed. Alternatively, a dangling symbolic link can cause a new target file to be created if the process has permission to write to the target directory.
Attack Path
- An attacker obtains write access to the directory from which the skill runs.
- The attacker creates
knowledge.jsonas either:- a symbolic link to an existing writable file conta ...[truncated 882 chars]
- Remediation
View remediation
Remediation Suggestions
- Store state in a dedicated application-data directory rather than the current working directory.
- Create that directory with restrictive permissions and verify its ownership before use.
- Use
os.lstat()to detect and reject symbolic links at the destination. - Open files using platform-appropriate no-follow protections such as
os.open()withO_NOFOLLOWwhere available. - Write data to a securely created temporary file in the same trusted directory, flush and synchronize it, and then atomically replace the destination with
os.replace(). - Apply restrictive file permissions, such as owner-only read and write access, where appropriate.
- Avoid executing the skill with elevated privileges.
