T09 · Insecure Skill Coding Practices
- Location
scripts/gen_openrouter.py:416- Finding
Unrestricted Output Directory Permits Writes Outside the Workspace
- Content
View full analysis
Vulnerability Details
File Location:
scripts/gen_openrouter.py, lines 416–432, with the argument exposed at lines 462–466 and used at lines 504–505
Vulnerability Type: Unrestricted filesystem output path
Risk Level: MediumComplete Code Snippet
python def ensure_output_dir(custom_dir: Optional[str], workspace_dir: Path) -> Path: if custom_dir: custom_path = Path(custom_dir).expanduser() if custom_path.is_absolute(): out_dir = custom_path.resolve() else: out_dir = (workspace_dir / custom_path).resolve() else: out_dir = (workspace_dir / "skills" / "imagen" / "out").resolve() out_dir.mkdir(parents=True, exist_ok=True) return out_dirThe destination is exposed as a command-line argument and used without a workspace-containment check:
python p.add_argument( "--out-dir", default=None, help="Directorio de salida. Si es relativo, se resuelve dentro del workspace. " "Por defecto: skills/imagen/out", )python out_dir = ensure_output_dir(args.out_dir, workspace_dir) out_file = save_image(raw, ext, out_dir, args.quality)Technical Analysis
The
--out-diroption accepts arbitrary absolute paths. Relative paths are combined with the workspace and normalized usingresolve(), but the normalized destination is never checked to ensure that it remains beneathworkspace_dir. A value containing traversal components, such as../../target, can therefore escape the intended workspace.The script subsequently creates the selected directory and writes the generated image there using the permissions of the Agent process. This behavior conflicts with the Skill's declared restriction that generated output must remain in the current Agent workspace, normally under
skills/imagen/out.Although the documented invocation does not use
--out-dir, the script exposes the option directly. Any caller or prompt-induced invocation capable of inf ...[truncated 1603 chars]- Remediation
View remediation
Remediation Suggestions
- Remove the
--out-diroption if custom output locations are not required by the declared functionality. - If customization is necessary, reject absolute paths and enforce containment after canonicalization:
python def ensure_output_dir(custom_dir: Optional[str], workspace_dir: Path) -> Path: workspace_dir = workspace_dir.resolve() relative_dir = Path(custom_dir) if custom_dir else Path("skills/imagen/out") if relative_dir.is_absolute(): raise ValueError("The output directory must be relative to the workspace.") out_dir = (workspace_dir / relative_dir).resolve() if not out_dir.is_relative_to(workspace_dir): raise ValueError("The output directory must remain inside the workspace.") out_dir.mkdir(parents=True, exist_ok=True) return out_dir- Consider restricting output to the fixed
workspace_dir / "skills" / "imagen" / "out"directory rather than merely any location under the workspace. - Account for symbolic-link races where hostile local users can modify workspace paths. On supported platforms, use directory file descriptors, no-follow semantics, or an equivalent safe file-creation mechanism.
- Create files with collision-safe names and restrictive permissions where the runtime environment requires stronger multi-user isolation.
- Add automated tests covering absolute paths,
..traversal, symbolic links, and valid in-workspace destinations.
- Remove the
