T09 · Insecure Skill Coding Practices
- Location
create_ai_team.py:474- Finding
Generated Files Can Escape the Project Boundary Through Symlinks and Arbitrary Log Paths
- Content
View full analysis
Vulnerability Details
File Location:
create_ai_team.py:474-476, 557-558, 623-624, 671-672, 734-735, 849-855;error_handler.py:27-34, 84-90
Vulnerability Type: Unconfined file write and symlink-following overwrite
Risk Level: MediumVulnerable Code
create_ai_team.py:474-476:python # Create main ai-team directory ai_team_dir = self.project_path / 'ai-team' safe_create_directory(ai_team_dir)create_ai_team.py:557-558:python with open(file_path, 'w', encoding='utf-8') as f: f.write(content)create_ai_team.py:623-624:python with open(file_path, 'w', encoding='utf-8') as f: f.write(content)create_ai_team.py:671-672:python with open(ai_team_dir / 'PROJECT_PROGRESS.md', 'w', encoding='utf-8') as f: f.write(content)create_ai_team.py:734-735:python with open(ai_team_dir / 'WORKFLOW.md', 'w', encoding='utf-8') as f: f.write(content)create_ai_team.py:849-855:python # Logging parser.add_argument('--log-file', help='Path to log file') parser.add_argument('--verbose', '-v', action='store_true', help='Enable verbose output') args = parser.parse_args() # Setup logging setup_logging(args.log_file)error_handler.py:27-34:python if log_file: logging.basicConfig( level=logging.INFO, format=log_format, handlers=[ logging.FileHandler(log_file, encoding='utf-8'), logging.StreamHandler(sys.stdout) ] )error_handler.py:84-90:python def safe_create_directory(path): """Safely create directory with error handling""" try: Path(path).mkdir(parents=True, exist_ok=True) return True except Exception as e: raise AITeamError(f"Failed to create directory {path}: {str(e)}")Technical Analysis
...[truncated 2869 chars]
- Remediation
View remediation
Remediation Suggestions
- Resolve the project root once with
Path.resolve(strict=True)and use it as the trusted filesystem boundary. - Before every directory creation or file write, resolve the destination or its nearest existing parent and verify containment with
Path.relative_to(trusted_root). Reject the operation if containment fails. - Reject symbolic links in every component from the project root through the final destination. Check components with
Path.is_symlink()oros.lstat()rather than relying on ordinary existence checks. - Open output files using no-follow semantics. On supported platforms, use
os.open()withO_NOFOLLOWand appropriate creation flags, then wrap the resulting descriptor withos.fdopen(). - Avoid blindly overwriting existing output files. Reject unexpected pre-existing files or require explicit user confirmation before replacement.
- Restrict
--log-fileto a location beneath the resolved project root, or remove the option and use a fixed project-local log path. - If external log paths are intentionally supported, clearly document the broader write scope and require explicit confirmation before opening them.
- Apply the same canonicalization, containment, and symlink checks to
safe_create_directory()and the project-specific error-log path inhandle_error(). - Add regression tests using symlinked directories and output files to confirm that out-of-project writes are rejected.
- Resolve the project root once with
