T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/main.py:444
- Finding
- Unrestricted Output Path Allows Arbitrary File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:412-416` and `scripts/main.py:444-447` **Vulnerability Type**: Unrestricted filesystem write **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument( "--output", "-o", help="Output file path (default: stdout)" ) ``` ```python if args.output: with open(args.output, "w", encoding="utf-8") as f: f.write(output) print(f"Report saved to: {args.output}") ``` ### Technical Analysis The `--output` argument accepts an arbitrary path and passes it directly to `open()` in truncating write mode. The implementation does not canonicalize the path, confine it to an approved output directory, reject traversal components, check for symbolic links, or prevent replacement of an existing file. This behavior contradicts the security checklist in `SKILL.md`, which identifies workspace-restricted output as a required control. Although the flaw does not grant privileges beyond those already held by the process, it exposes every file writable by the current operating-system account to replacement. ### Attack Path 1. An attacker controls or influences the arguments used to invoke the Skill. 2. The attacker supplies an output path targeting an existing writable file, for example: ```bash python scripts/main.py --target BRCA1 \ --output ../../writable-project/config.json \ --format json ``` 3. Path traversal resolves outside the intended project or output directory. 4. `open(args.output, "w")` opens the destination in truncation mode. 5. The existing file is erased and replaced with the generated report. A symbolic-link attack is also possible when an attacker can create a link at the selected output location: the script follows the link and writes to its target. ### Impact Assessment The attacker can overwrite files accessible to the operating-system identity running the Skill. Potential consequences include: - Destruction or corruption of projec ...[truncated 423 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Create a dedicated output directory under the Skill workspace. - Resolve both the approved directory and requested destination with `pathlib.Path.resolve()`. - Reject any destination that is not a descendant of the approved output directory. - Reject absolute paths unless explicitly required and validated. - Prevent symbolic-link traversal by checking path components and using platform-appropriate no-follow file-opening controls. - Avoid silently replacing existing files. Use exclusive creation mode (`"x"`) unless overwrite behavior is explicitly authorized. - If replacement is required, write to a securely created temporary file in the same directory and atomically rename it after validation. - Return a generic error that does not unnecessarily expose internal filesystem paths. Example confinement check: ```python from pathlib import Path output_root = (Path.cwd() / "output").resolve() output_root.mkdir(parents=True, exist_ok=True) destination = (output_root / args.output).resolve() if output_root not in destination.parents: raise ValueError("Output path must remain inside the output directory") with destination.open("x", encoding="utf-8") as file: file.write(output) ``` ]]>
