T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/output_generator.py:311
- Finding
- Arbitrary File Write Through Unvalidated Skill Name<![CDATA[ ## Vulnerability Details **File Location**: `scripts/output_generator.py:311-318` **Vulnerability Type**: Path traversal leading to arbitrary file creation or overwrite **Risk Level**: High ### Vulnerable Code ```python def create_skill_file(skill_name: str, skill_content: str) -> Path: """Create a new skill file in the project's .claude/skills/ directory.""" ensure_directories() skill_dir = get_project_skills_dir() / skill_name skill_dir.mkdir(parents=True, exist_ok=True) skill_path = skill_dir / 'SKILL.md' skill_path.write_text(skill_content) return skill_path ``` ### Technical Analysis The `skill_name` argument is accepted directly from the `--create-skill` command-line option and joined to the intended `.claude/skills` directory without validation or containment checks. `pathlib.Path` does not prevent traversal components such as `..`. In addition, if `skill_name` is an absolute path, joining it to the base path discards the base path. Consequently, the resulting destination can escape the intended skills directory. The final filename is always `SKILL.md`, but an attacker can still select an arbitrary parent directory reachable by the current user and overwrite an existing `SKILL.md`. This is especially security-sensitive because such files may contain persistent Agent instructions. ### Attack Path 1. An attacker or untrusted automation gains the ability to control the `--create-skill` value and `--content`. 2. The attacker supplies a traversal or absolute path, for example: ```bash python scripts/output_generator.py \ --create-skill "../../../../tmp/attacker-controlled" \ --content "attacker-controlled skill instructions" ``` 3. `get_project_skills_dir() / skill_name` resolves outside `.claude/skills`. 4. The program creates the selected directory if necessary. 5. It writes attacker-controlled content to the resulting `SKILL.md`. 6. If the selected destination is an Agent-discovered skill ...[truncated 709 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Restrict skill names to a safe slug format: ```python import re if not re.fullmatch(r"[a-z0-9][a-z0-9-]{0,63}", skill_name): raise ValueError("Invalid skill name") ``` 2. Resolve and verify path containment before creating directories: ```python base_dir = get_project_skills_dir().resolve() skill_dir = (base_dir / skill_name).resolve() if skill_dir.parent != base_dir: raise ValueError("Skill destination escapes the skills directory") ``` 3. Reject absolute paths and any name containing path separators or `..`. 4. Refuse to overwrite an existing `SKILL.md` unless the user explicitly authorizes replacement. 5. Write through a securely created temporary file and atomically replace the destination. 6. Add tests covering absolute paths, repeated traversal components, encoded separators, symlinks, and existing destination files. 7. If generated content can originate from conversation text, keep explicit human review mandatory before writing it as an executable Agent skill. ]]>
