T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/portfolio.py:24
- Finding
- Portfolio Data Is Written Without Explicitly Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/portfolio.py:24, 34-39` **Vulnerability Type**: Insecure storage of sensitive local data **Risk Level**: Medium ### Vulnerable Code ```python PORTFOLIO_FILE = Path.home() / ".clawdbot" / "skills" / "a-stock-analysis" / "portfolio.json" ``` ```python def save_portfolio(data: dict): """保存持仓数据""" PORTFOLIO_FILE.parent.mkdir(parents=True, exist_ok=True) data["updated_at"] = datetime.now().isoformat() with open(PORTFOLIO_FILE, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The portfolio file contains potentially sensitive financial information, including stock identifiers, acquisition costs, quantities, and timestamps. The application creates its storage directory and file without specifying restrictive permissions. The resulting permissions are inherited from the process umask. Under a common umask of `022`, the directory may be created with mode `0755` and the file with mode `0644`, allowing other local users to traverse the directory and read the portfolio data. The write operation also follows an existing symbolic link. If an attacker can write to the portfolio directory—for example, because it was previously created with unsafe ownership or permissions—the attacker could place a symbolic link at the portfolio path and cause subsequent saves to overwrite a file accessible to the victim account. ### Attack Path A local disclosure path is as follows: 1. The victim runs a portfolio command that invokes `save_portfolio`. 2. The application creates the directory and `portfolio.json` using permissions derived from the current umask. 3. With a permissive umask, the resulting directory and file are readable by other local accounts. 4. Another local user traverses the directory and reads `portfolio.json`. 5. The attacker obtains the victim's stock codes, position sizes, acquisition costs, and portfolio timestamps. A s ...[truncated 986 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the portfolio directory with mode `0700` and verify its ownership and permissions before use. 2. Create portfolio files with mode `0600`, independent of the process umask. 3. Write updates to a securely created temporary file in the same directory, flush and synchronize it, and atomically replace the destination with `os.replace`. 4. Refuse to operate on symbolic links and verify the destination with `lstat`. 5. Correct permissions on existing installations. 6. Handle write failures without leaving partially written portfolio data. Example hardening measures include: ```python PORTFOLIO_FILE.parent.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(PORTFOLIO_FILE.parent, 0o700) ``` For new files, use `os.open` with `O_CREAT | O_EXCL | O_WRONLY` and mode `0o600`, or use a secure temporary file followed by atomic replacement. Validate that the directory and destination are owned by the current user before writing. ]]>
