T09 · Insecure Skill Coding Practices
Warning
- Location
- proof_manager.py:458
- Finding
- Arbitrary File Overwrite Through User-Controlled VSL Output Path## Vulnerability Details **File Location**: `proof_manager.py`, lines 458-460; related argument declaration at line 938 **Vulnerability Type**: Arbitrary file overwrite / unrestricted path handling **Risk Level**: Medium ### Vulnerable Code ```python # If VSL output requested if platform == "vsl" and args.output: with open(args.output, "w") as f: f.write(story) print(f" ✅ VSL script: {args.output}") ``` The output path is supplied through this unrestricted command-line argument: ```python p_story.add_argument("--output", help="Output file (for VSL)") ``` ### Technical Analysis The `--output` argument controls the complete path passed to `open()` in write mode. The application does not canonicalize the path, restrict it to an approved output directory, reject symbolic links, or verify that the destination is a regular file. Opening a destination with mode `"w"` creates the file if it does not exist and truncates it if it does. Consequently, a caller capable of invoking the CLI can overwrite any file writable by the process. Relative traversal sequences such as `../../...`, absolute paths, and links pointing outside the intended VSL output directory are all accepted. This behavior exceeds the documented purpose of generating VSL scripts and can also exceed the write locations declared by the Skill metadata. ### Attack Path 1. Ensure the proof vault contains at least one item whose `impact_score` meets the story-generation threshold. 2. Identify a file writable by the Skill process. 3. Invoke the story command with the VSL platform and the target file as the output path, for example: ```bash python3 proof_manager.py story \ --platform vsl \ --filter-impact 5 \ --output /workspace/path/to/writable-target ``` 4. `cmd_story()` generates a story and calls `open(args.output, "w")`. 5. The target is truncated and replaced with generated Markdown content. ...[truncated 850 chars]
- Remediation
- ## Remediation Suggestions 1. Restrict VSL output to an explicitly approved directory, such as `/workspace/voice/scripts/`. 2. Resolve both the approved directory and requested destination with `pathlib.Path.resolve()`, then verify that the destination remains beneath the approved directory: ```python from pathlib import Path VSL_DIR = Path("/workspace/voice/scripts").resolve() requested = Path(args.output) destination = requested.resolve() if VSL_DIR not in destination.parents: raise ValueError("Output path must be inside the approved VSL directory") ``` 3. Reject existing symbolic links and verify that existing destinations are regular files. 4. Prefer accepting only a filename from the caller and construct the complete path internally. 5. Use atomic writes through a securely created temporary file in the destination directory, followed by `os.replace()`. 6. Consider refusing to replace an existing file unless the caller supplies a separate, explicit overwrite option. 7. Align the runtime filesystem permissions with the write paths declared in `SKILL.md`.
