T09 · Insecure Skill Coding Practices
- Location
scripts/test_runner.py:288- Finding
Fixed Output Path Permits Symlink-Based File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/test_runner.py, lines 288-291
Vulnerability Type: CWE-59 — Improper Link Resolution Before File Access
Risk Level: MediumVulnerable Code
python # Save to JSON result_dict = asdict(result) with open("/home/gem/.aily/workspace/skills/voight-kampff-test/results/demo_result.json", "w") as f: json.dump(result_dict, f, indent=2, ensure_ascii=False)Technical Analysis
The demonstration routine writes its result to a fixed, absolute path using
open(..., "w"). This mode follows symbolic links and truncates the resolved destination before writing. The code does not verify that the destination is a regular file, validate ownership, prevent symbolic-link traversal, or create the output securely.If another local user can control the
resultsdirectory or replacedemo_result.jsonwith a symbolic link, running the script under a more privileged account can overwrite a file accessible to that account. The written content is constrained to the generated JSON report, so this is not an arbitrary-content write; nevertheless, truncation and replacement of the target's contents may cause data loss or service disruption.The hardcoded environment-specific path can also cause execution failure when the directory does not exist, although that reliability problem is secondary to the unsafe file-handling issue.
Attack Path
-
An attacker obtains write access to
/home/gem/.aily/workspace/skills/voight-kampff-test/results/or otherwise controlsdemo_result.json. -
The attacker creates a symbolic link from
demo_result.jsonto a file writable by the account expected to run the skill:bash ln -s /path/to/target /home/gem/.aily/workspace/skills/voight-kampff-test/results/demo_result.json -
A more privileged user or agent runs:
bash python scripts/test_runner.py -
Python follows the symbolic link ...[truncated 922 chars]
-
- Remediation
View remediation
Remediation Suggestions
- Do not write demonstration output to a hardcoded absolute path. Accept an explicit output path from the caller or use a directory owned exclusively by the current user.
- Create the destination directory with restrictive permissions and verify its ownership before writing.
- Refuse to follow symbolic links. On supported platforms, open the file using
os.open()withO_NOFOLLOW,O_CREAT, andO_EXCL, then wrap the descriptor withos.fdopen(). - Validate the opened file with
os.fstat()and ensure that it is a regular file owned by the expected user. - Prefer atomic output: securely create a temporary file in the same trusted directory, flush and synchronize it, and then use
os.replace()after validating the destination. - Apply restrictive file permissions, such as
0o600, if reports may contain subject responses or other sensitive information. - For a demonstration script that does not need persistence, print the report only or use Python's
tempfilemodule rather than a predictable shared path.
A hardened approach should resemble:
python import os from pathlib import Path output_dir = Path.home() / ".local" / "share" / "voight-kampff-test" output_dir.mkdir(mode=0o700, parents=True, exist_ok=True) output_path = output_dir / "demo_result.json" flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL if hasattr(os, "O_NOFOLLOW"): flags |= os.O_NOFOLLOW fd = os.open(output_path, flags, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(result_dict, f, indent=2, ensure_ascii=False)If overwriting an existing report is required, use a securely created temporary file and an atomic replacement strategy rather than opening a predictable destination directly.
