T09 · Insecure Skill Coding Practices
- Location
scripts/archive_project.py:22- Finding
Path Traversal Allows Reading and Deleting Files Outside the Knowledge Base
- Content
View full analysis
Vulnerability Details
File Location:
scripts/archive_project.py, lines 22-59
Vulnerability Type: Improper path validation and arbitrary file deletion
Risk Level: HighVulnerable Code
python # Resolve paths if not project_file.is_absolute(): project_file = kb_path / project_file if not project_file.exists(): print(f"❌ Project file not found: {project_file}") sys.exit(1) # Determine source and destination archives_path = kb_path / "archives" if not archives_path.exists(): archives_path.mkdir(parents=True) print(f"✅ Created archives/ directory") # Create archived filename with date date_str = datetime.now().strftime("%Y-%m-%d") archived_name = f"{project_file.stem}-archived-{date_str}{project_file.suffix}" dest_file = archives_path / archived_name # Read original content content = project_file.read_text() # Add archive metadata at top archive_note = f"""--- ARCHIVED: {datetime.now().strftime("%Y-%m-%d")} Original location: {project_file.relative_to(kb_path)} --- """ archived_content = archive_note + content # Write to archives dest_file.write_text(archived_content) print(f"✅ Archived to: {dest_file.relative_to(kb_path)}") # Remove original project_file.unlink()Technical Analysis
The script joins the user-controlled
project_fileargument tokb_pathwithout canonicalizing the resulting path or verifying that it remains within the knowledge base. A relative path containing..components can therefore escape the expectedprojects/active/directory and the knowledge-base root.For example, if
kb_pathis/home/user/kb, the argument../important.txtproduces the lexical path/home/user/kb/../important.txt. The path passes the existence check, can be read, and remains lexically compatible withrelative_to(kb_path)because it begins with the knowledge-base path before normalization. The script then copies the content intoarchives/and permanently removes the original usingunlink()....[truncated 1183 chars]
- Remediation
View remediation
Remediation Suggestions
- Resolve the knowledge-base root and candidate source before use:
python kb_root = kb_path.resolve(strict=True) active_root = (kb_root / "projects" / "active").resolve(strict=True) source = (kb_root / project_file).resolve(strict=True) - Reject absolute user-supplied project paths unless there is an explicit need to support them.
- Require the resolved source to be contained in
projects/active/:python if not source.is_relative_to(active_root): raise ValueError("Project must be located under projects/active/") - Require the source to be a regular file and define an explicit symlink policy. Reject symlinks when they are unnecessary.
- Resolve and validate the archive destination before writing.
- Refuse destination collisions instead of silently overwriting an archive created on the same date.
- Use an atomic move operation where possible rather than writing a copy and subsequently deleting the source.
- Add tests covering
.., absolute paths, symlinks, nonexistent files, and destination collisions.
- Resolve the knowledge-base root and candidate source before use:
