T09 · Insecure Skill Coding Practices
- Location
scripts/process_digest.py:197- Finding
Caller-Controlled Output Directory Enables Recursive Deletion of Unrelated Files
- Content
View full analysis
Vulnerability Details
File Location:
scripts/process_digest.py:197-209
Vulnerability Type: Unrestricted recursive file deletion
Risk Level: HighVulnerable Code
python def cleanup_outputs(output_dir: Path, days: int) -> int: if days < 0 or not output_dir.exists(): return 0 cutoff = datetime.now() - timedelta(days=days) removed = 0 for path in output_dir.rglob("*"): if not path.is_file(): continue try: modified = datetime.fromtimestamp(path.stat().st_mtime) except OSError: continue if modified < cutoff: path.unlink() removed += 1 return removedThe affected directory is supplied through a command-line argument:
python parser.add_argument("--output-dir", default=str(SKILL_DIR / ".temp")) parser.add_argument( "--cleanup-days", type=int, help="Clean historical output files older than N days" )Technical Analysis
The cleanup routine recursively enumerates every file beneath
output_dirand deletes files based solely on modification time. Although the default output directory is the Skill's.tempdirectory,--output-diris caller-controlled and is not resolved or restricted to that directory.Consequently, cleanup authority is not limited to files created by this Skill. Any directory readable and writable by the Agent process can become the deletion root. The function also does not restrict deletion to expected output filename patterns such as
emails_*.json,analysis_*.json, orsummary_*.md.This exceeds the minimum filesystem privileges required for cleaning up generated digest artifacts.
Attack Path
- An attacker, malicious instruction, or mistaken automation causes the Agent to invoke
process_digest.py. - The invocation supplies a sensitive writable directory through
--output-dir. - It also su ...[truncated 917 chars]
- An attacker, malicious instruction, or mistaken automation causes the Agent to invoke
- Remediation
View remediation
Remediation Suggestions
-
Resolve the requested directory to a canonical absolute path and require it to remain beneath the Skill-owned
.tempdirectory:python allowed_root = (SKILL_DIR / ".temp").resolve() requested = output_dir.resolve() if requested != allowed_root and allowed_root not in requested.parents: raise ValueError("Cleanup is restricted to the Skill temporary directory.") -
Refuse filesystem roots, home directories, the project root, and symbolic-link traversal.
-
Delete only recognized artifacts created by the Skill, using strict filename patterns and expected extensions.
-
Keep cleanup separate from normal digest processing and require explicit confirmation when a non-default directory is requested.
-
Record a dry-run list before deletion and provide a
--dry-runoption. -
Where practical, maintain a manifest of generated files and delete only entries from that manifest.
-
