T05 · Unauthorized Access and Privilege Escalation
Warning
- Location
- scripts/scan_skill.py:163
- Finding
- Project Boundary Escape Through Symbolic Links<![CDATA[ ## Vulnerability Details **File Location**: `scripts/scan_skill.py:163-190` and `scripts/scan_skill.py:284-294` **Vulnerability Type**: Symbolic-link traversal and unauthorized file access **Risk Level**: Medium ### Vulnerable Code ```python def Scan_Supporting_Files(skill_dir: Path) -> list[Finding]: """Scan all supporting files in the skill directory.""" findings: list[Finding] = [] # Scan all files in scripts/ and other subdirectories Scannable_Extensions = {".py", ".sh", ".bash", ".js", ".ts", ".rb", ".pl"} for file_path in skill_dir.rglob("*"): if not file_path.is_file(): continue if file_path.name == "SKILL.md": continue str_path = str(file_path) # Check for executable permissions on non-standard files if file_path.suffix not in Scannable_Extensions: try: file_stat = os.stat(file_path) if file_stat.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH): findings.append(Finding( pattern_name="executable_non_script", severity=Severity.MEDIUM, category=Category.SKILL_INJECTION, description=f"Non-standard file has executable permission: {file_path.name}", file_path=str_path, line_number=0, matched_text=f"mode: {oct(file_stat.st_mode)}", )) except OSError: pass # Scan content of script files if file_path.suffix in Scannable_Extensions: try: content = file_path.read_text(encoding="utf-8", errors="replace") except (PermissionError, OSError): continue ``` The main skill file is read with the same issue: ```python skill_md = skill_dir / "SKILL.md" if not skill_md.exists(): print(f"Error: No SKILL.md found in {skill_dir}", file=sys.stderr) sys.exit(1) print(f"Analyzing skill: {skill_dir}\n") all_findings: list[Finding] = [] str_skill_md = str(skill_md) # Read SKILL.md content = skill_md.read_text(encoding="utf-8", errors="replace") ``` ### Technical Analysis `Path.is_file()`, `Path.exists()`, `Path.read_text()`, and `os.stat()` follo ...[truncated 1920 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Reject symbolic links before reading or statting files: ```python if file_path.is_symlink(): continue ``` - Resolve both the root and every candidate, then enforce containment: ```python root = skill_dir.resolve(strict=True) def safe_resolve(path: Path) -> Path: if path.is_symlink(): raise ValueError(f"Symbolic links are not allowed: {path}") resolved = path.resolve(strict=True) if not resolved.is_relative_to(root): raise ValueError(f"Path escapes skill directory: {path}") return resolved ``` - Apply the same check to `SKILL.md`, all supporting files, and inventory operations. - Prefer file-descriptor-based opening with no-follow semantics where supported, such as `O_NOFOLLOW`, to reduce time-of-check/time-of-use races. - Run the scanner in a sandbox with a read-only view containing only the target directory. Do not expose the user's home directory, SSH directory, cloud credentials, or other unrelated paths. - Add regression tests covering absolute symlinks, relative symlinks, nested symlink chains, broken links, and links changed between validation and opening. ]]>
