T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- scripts/fixer.py:74
- Finding
- Unrestricted Scanner-Supplied File Access and Modification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fixer.py:74-77`, `scripts/fixer.py:255-273` **Vulnerability Type**: Unvalidated path usage leading to arbitrary file read and write **Risk Level**: High ### Vulnerable Code ```python file_path = Path(vuln.file) try: content = file_path.read_text(encoding='utf-8') ``` ```python def _apply_fix(self, fix: Fix) -> None: """Apply a single fix to a file.""" # Create backup if enabled if self.backup: backup_path = fix.file_path.with_suffix(fix.file_path.suffix + '.bak') shutil.copy2(fix.file_path, backup_path) # Read file content = fix.file_path.read_text(encoding='utf-8') lines = content.split('\n') # Replace lines new_lines = lines[:fix.line_start-1] new_lines.extend(fix.fixed_code.split('\n')) new_lines.extend(lines[fix.line_end:]) # Write back fix.file_path.write_text('\n'.join(new_lines), encoding='utf-8') ``` ### Technical Analysis The path contained in `vuln.file` is accepted directly from the external scanner result and converted to a `Path` without validation. The implementation does not resolve the path against `self.skill_path`, verify that it remains under the requested skill directory, reject absolute paths, or detect symlinks that escape the directory. Consequently, a malicious or compromised `audit` implementation can submit an absolute path such as `/home/user/.bashrc` or a traversal path such as `../../sensitive-file`. The fixer will read that file during fix generation and may subsequently create a backup and overwrite it during fix application. The required `audit` module is not included in this project, so its trustworthiness and validation behavior cannot be established from the audited source. ### Attack Path 1. An attacker influences the target being scanned or compromises/spoofs the imported `audit` module. 2. The scanner returns a vulnerability whose `file` field points outside the inte ...[truncated 881 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Resolve the configured skill root once with `skill_root = self.skill_path.resolve(strict=True)`. - Resolve every scanner-supplied path before reading or writing it. - Require each candidate path to be contained by the skill root using `Path.relative_to()` or `Path.is_relative_to()`. - Reject absolute scanner paths unless they resolve inside the approved root. - Detect and reject symlink-based escapes. Revalidate containment immediately before each write to reduce time-of-check/time-of-use exposure. - Validate scanner output against a strict schema, including path type, line bounds, vulnerability identifier, and expected source text. - Open files defensively and avoid following symlinks where the operating system supports that behavior. - Apply least privilege by running the fixer under an account that cannot modify unrelated sensitive files. Example containment check: ```python skill_root = self.skill_path.resolve(strict=True) candidate = Path(vuln.file) if not candidate.is_absolute(): candidate = skill_root / candidate candidate = candidate.resolve(strict=True) try: candidate.relative_to(skill_root) except ValueError: raise ValueError(f"Scanner path escapes skill root: {candidate}") ``` ]]>
