T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- skill_evolver/analyzer.py:74
- Finding
- Unvalidated Skill Name Allows Directory-Boundary Bypass<![CDATA[ ## Vulnerability Details **File Location**: `skill_evolver/analyzer.py:74`, with recursive file access at `skill_evolver/analyzer.py:258` **Vulnerability Type**: Path traversal and unauthorized filesystem analysis **Risk Level**: High ### Vulnerable Code ```python def analyze_skill(self, skill_name: str) -> SkillAnalysisResult: """ Analyze a single skill. """ skill_path = self.skills_dir / skill_name result = SkillAnalysisResult( skill_name=skill_name, skill_path=str(skill_path) ) if not skill_path.exists(): result.issues.append(AnalysisIssue( severity="critical", category="structure", message=f"Skill directory does not exist: {skill_path}" )) return result result.exists = True self._analyze_skill_md(result, skill_path) self._analyze_package_json(result, skill_path) self._analyze_python_code(result, skill_path) self._analyze_structure(result, skill_path) ``` The resulting path is subsequently traversed recursively: ```python def _analyze_python_code(self, result: SkillAnalysisResult, skill_path: Path): """Analyze Python source files.""" python_files = list(skill_path.rglob("*.py")) if not python_files: result.issues.append(AnalysisIssue( severity="info", category="structure", message="No Python source files found" )) return for py_file in python_files: try: content = py_file.read_text(encoding="utf-8") try: ast.parse(content) except SyntaxError as e: result.issues.append(AnalysisIssue( severity="critical", category="syntax", message=f"Python syntax error in {py_file.name}: {e}", line=e.lineno )) continue ``` ### Technical Analysis `skill_name` is joined directl ...[truncated 2059 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Reject absolute skill names and names containing path separators or parent-directory components. 2. Resolve both the configured root and candidate path before accessing the filesystem. 3. Require the resolved candidate to remain beneath the resolved root: ```python def _resolve_skill_path(self, skill_name: str) -> Path: if not skill_name or Path(skill_name).is_absolute(): raise ValueError("Invalid skill name") root = self.skills_dir.resolve() candidate = (root / skill_name).resolve() try: candidate.relative_to(root) except ValueError as exc: raise ValueError("Skill path escapes the configured skills directory") from exc return candidate ``` 4. Consider enforcing a strict skill-name pattern such as `^[A-Za-z0-9][A-Za-z0-9._-]*$`. 5. Define and enforce a symlink policy. If symlinks are not required, reject skill directories and recursively encountered files that resolve outside the root. 6. Limit the number and total size of files analyzed to reduce denial-of-service exposure. 7. Add tests for absolute paths, `../` traversal, nested traversal, symlink escapes, and valid names. ]]>
