T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/rebuild_critical_fact_cards.py:108
- Finding
- Unrestricted Recursive Directory Deletion Through User-Controlled Output Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/rebuild_critical_fact_cards.py:108-116` **Vulnerability Type**: Arbitrary recursive directory deletion **Risk Level**: High ### Vulnerable Code ```python parser = argparse.ArgumentParser(description='Rebuild critical-fact object cards from critical-facts/*.md with tolerant parsing.') parser.add_argument('--critical-facts-dir', default=str(DEFAULT_CRITICAL_FACTS_DIR), help='Directory containing critical-facts markdown files') parser.add_argument('--cards-dir', default=str(DEFAULT_CARDS_DIR), help='Output directory for rebuilt cards') args = parser.parse_args() critical_facts_dir = Path(args.critical_facts_dir) cards_dir = Path(args.cards_dir) if cards_dir.exists(): shutil.rmtree(cards_dir) ``` ### Technical Analysis The `--cards-dir` command-line argument accepts an arbitrary filesystem path. The supplied path is passed directly to `shutil.rmtree()` without canonicalization, workspace confinement, symlink protection, or validation that it refers to the intended `critical-facts/cards` directory. `shutil.rmtree()` recursively deletes the target and all of its contents. Consequently, any process or Agent capable of influencing the command arguments can convert this maintenance utility into a destructive filesystem operation. The vulnerability does not require shell metacharacters because the dangerous behavior is implemented directly by the Python process. The default invocation uses the expected cards directory, but the exposed unrestricted option makes the script unsafe when invoked directly or through an Agent-generated command. ### Attack Path 1. An attacker causes an Agent, automation process, or operator to invoke `rebuild_critical_fact_cards.py`. 2. The attacker influences the `--cards-dir` argument, for example by supplying a path to another workspace directory. 3. The script converts the supplied string into a `Path` without checking its resolved location. 4. If the selected dire ...[truncated 872 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove the configurable `--cards-dir` option unless custom output directories are required. 2. Resolve and validate the path before deletion: - Call `Path.resolve(strict=False)`. - Require the resolved path to be a strict descendant of the expected `critical-facts` directory. - Reject the filesystem root, user home, workspace root, input directory, and all parent directories. 3. Reject symlinks and verify every relevant path component before deletion. 4. Refuse recursive deletion when the path differs from the expected default unless an explicit administrative confirmation flag is present. 5. Prefer rebuilding into a newly created temporary directory and atomically replacing the old cards directory. 6. Add regression tests for absolute paths, `..` traversal, symlink targets, the workspace root, the home directory, and the filesystem root. 7. Run the Skill under an account with write access limited to its intended workspace. ]]>
