T09 · Insecure Skill Coding Practices
- Location
scripts/run.py:47- Finding
Workspace Escape and Arbitrary File Overwrite via Unvalidated Output Path
- Content
View full analysis
None: ...[truncated 3213 chars]- Remediation
View remediation
Path: if not relative: raise ValueError("Output path must not be empty") supplied = Path(relative) if supplied.is_absolute(): raise ValueError("Absolute output paths are not allowed") root = workspace.resolve() destination = (root / supplied).resolve() try: destination.relative_to(root) except ValueError as exc: raise ValueError( f"Output path escapes the workspace: {relative!r}" ) from exc return destination ``` 2. Use this function instead of directly joining the supplied value: ```python outputs = parse_semicolon_list(args.outputs) or ["papers/papers_raw.jsonl"] out_path = workspace_path(workspace, outputs[0]) ``` 3. Prefer a strict allowlist because this Skill declares a fixed output contract: ```python ALLOWED_OUTPUTS = {"papers/papers_raw.jsonl"} if outputs[0] not in ALLOWED_OUTPUTS: raise ValueError(f"Unsupported output path: {outputs[0]!r}") ``` 4. Repeat validation in `tooling/executor.py`. Entry-point validation remains mandatory, but executor-side validation provides defense in depth and rejects malicious `UNITS.csv` content earlier. 5. Validate the derived CSV path separately and ensure it remains in the workspace. 6. Consider symlink-aware hardening where workspaces may be attacker-controlled. Resolve the destination's parent and verify it remains below the resolved workspace immediately before writing. 7. Add regression tests covering: - `../../outside.jsonl` - Absolute paths - Nested traversal paths - Symlinked output directories - The valid `papers/papers_raw.jsonl` destination - Validation of the derived CSV destination ]]>
