T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/create_note.py:176
- Finding
- Workspace Escape Allows Arbitrary Markdown File Creation## Vulnerability Details **File Location**: `scripts/create_note.py`, lines 176–183 and 277–278 **Vulnerability Type**: Path traversal and missing workspace containment validation **Risk Level**: Medium ### Vulnerable Code ```python # Determine output directory if output_dir is None: output_dir = foam_root elif not output_dir.is_absolute(): output_dir = foam_root / output_dir output_dir.mkdir(parents=True, exist_ok=True) # Generate filename slug = slugify(title) date_str = datetime.now().strftime("%Y-%m-%d") filename = f"{slug}.md" filepath = output_dir / filename # Check for existing file counter = 1 original_filepath = filepath while filepath.exists(): filename = f"{slug}-{counter}.md" filepath = output_dir / filename counter += 1 # Write the file filepath.write_text(content) print(f"Created: {filepath.relative_to(foam_root)}") ``` ### Technical Analysis The `--dir` argument is documented as relative to the Foam workspace, but the implementation accepts both absolute paths and relative paths containing `..`. Relative paths are joined to `foam_root` without normalization or a subsequent containment check. Absolute paths bypass the workspace join entirely. The script creates the destination directory and writes the file before calling `relative_to(foam_root)`. Consequently, the final display operation may raise an exception for an escaped path, but the external file has already been created. The generated filename is constrained by `slugify()` and receives a `.md` extension. Existing files are not overwritten because a numeric suffix is selected. Nevertheless, an attacker can create Markdown files and parent directories at arbitrary writable locations. ### Attack Path 1. Identify a directory writable by the account executing the Skill. 2. Invoke the script with an absolute or traversal-based output directory, for example: ```bash python3 scripts/create_n ...[truncated 729 chars]
- Remediation
- ## Remediation Suggestions - Reject absolute values for `--dir` because the option is documented as workspace-relative. - Resolve both the workspace and destination before creating directories: ```python root = foam_root.resolve(strict=True) destination = (root / output_dir).resolve() if not destination.is_relative_to(root): raise ValueError("Output directory must remain inside the Foam workspace") ``` - Repeat containment validation on the final file path immediately before writing. - Consider rejecting path components equal to `..` for clearer user-facing validation. - Avoid following symlinks that lead outside the workspace. Validate the resolved destination after its parent exists. - Perform the validation before `mkdir()` so escaped directories are not created.
