T09 · Insecure Skill Coding Practices
- Location
scripts/main.py:119- Finding
Documented Workspace Path Restrictions Are Not Enforced
- Content
View full analysis
Vulnerability Details
File Location:
scripts/main.py:119-125andscripts/main.py:276-280
Related Security Contract:SKILL.md:104
Vulnerability Type: Missing path traversal and workspace-boundary validation
Risk Level: MediumVulnerable Code
The input-loading logic checks only whether a path exists. It does not reject traversal components, absolute paths, or resolved paths outside the permitted workspace:
python def _load_image(self, path: str) -> Image.Image: """Load an image from file.""" img_path = Path(path) if not img_path.exists(): raise FileNotFoundError(f"Image not found: {path}") img = Image.open(path)The output logic likewise accepts any path available to the current process, creates its parent directories, and saves the result without a workspace-boundary check:
python # Save with DPI info output_path = Path(output) output_path.parent.mkdir(parents=True, exist_ok=True) # Save as PNG with DPI composite.save(output_path, dpi=(self.dpi, self.dpi))This contradicts the documented restriction in
SKILL.md:104:text - If any input file path contains `../` or points outside the workspace, reject with a path traversal warning.Technical Analysis
Filesystem access should be constrained by resolving each user-controlled path and verifying that the canonical path remains beneath a trusted workspace root. The implementation performs no such verification.
Path.exists()establishes only that the input exists. It does not establish that the path is authorized. Consequently, absolute paths, paths containing../, and symlinks resolving outside the workspace are accepted. The same issue applies to the output path, wheremkdir()andImage.save()operate on a caller-selected location.Merely searching for the literal string
../would not be sufficient because absolute paths, platform-specific separator ...[truncated 1524 chars]- Remediation
View remediation
Remediation Suggestions
- Require the application to receive or define an explicit trusted workspace root.
- Canonicalize the workspace, every input path, and the output path with
Path.resolve(). - Verify containment using
Path.relative_to()rather than string-prefix comparisons. - Apply the same policy to both input and output paths before opening files or creating directories.
- Reject paths whose symlink-resolved targets leave the workspace.
- Optionally reject symlink inputs entirely if they are unnecessary.
- Validate output extensions against the explicitly supported formats.
- Add automated tests for absolute paths,
../traversal, nested traversal, sibling-prefix paths, and symlinks escaping the workspace.
Example containment helper:
python def resolve_in_workspace(path: str, workspace: Path) -> Path: root = workspace.resolve(strict=True) candidate = Path(path).resolve(strict=False) try: candidate.relative_to(root) except ValueError: raise ValueError(f"Path is outside the workspace: {path}") return candidateFor inputs, additionally require
candidate.is_file()and resolve with strict existence checks. For output, validate containment before callingmkdir()orsave().
