diff --git a/src/plugin_system/plugin_loader.py b/src/plugin_system/plugin_loader.py index 5ddc220a..9e2be865 100644 --- a/src/plugin_system/plugin_loader.py +++ b/src/plugin_system/plugin_loader.py @@ -146,9 +146,20 @@ class PluginLoader: requirements_file = plugin_dir / "requirements.txt" if not requirements_file.exists(): return True # No dependencies needed - + + # Resolve and validate plugin_dir before constructing derived paths from it + try: + plugin_dir_resolved = plugin_dir.resolve(strict=True) + except OSError: + self.logger.error("Plugin directory does not exist: %s", plugin_dir) + return False + marker_path = plugin_dir_resolved / ".dependencies_installed" + try: + marker_path.relative_to(plugin_dir_resolved) + except ValueError: + return False + # Check if already installed - marker_path = plugin_dir / ".dependencies_installed" if marker_path.exists(): self.logger.debug("Dependencies already installed for %s", plugin_id) return True diff --git a/web_interface/blueprints/api_v3.py b/web_interface/blueprints/api_v3.py index 2222aa84..ffa5dbf9 100644 --- a/web_interface/blueprints/api_v3.py +++ b/web_interface/blueprints/api_v3.py @@ -7048,7 +7048,7 @@ _BACKUP_EXPORT_DIR = PROJECT_ROOT / "config" / "backups" / "exports" def _safe_backup_path(filename: str) -> Path: """Resolve a filename to an absolute path inside the export dir, rejecting any traversal attempts. Returns None if unsafe.""" - if not filename or '/' in filename or '\\' in filename or filename.startswith('.'): + if not filename or not re.match(r'^[a-zA-Z0-9][a-zA-Z0-9._-]{0,200}\.zip$', filename): return None path = (_BACKUP_EXPORT_DIR / filename).resolve() try: @@ -7124,7 +7124,18 @@ def backup_validate(): if not ok: logger.warning("Backup validation failed: %s", err_msg) return jsonify({'status': 'error', 'message': 'Invalid or corrupted backup file'}), 400 - return jsonify({'status': 'success', 'data': manifest}) + safe_manifest = { + 'schema_version': manifest.get('schema_version'), + 'created_at': manifest.get('created_at'), + 'ledmatrix_version': manifest.get('ledmatrix_version'), + 'hostname': manifest.get('hostname'), + 'contents': manifest.get('contents', []), + 'detected_contents': manifest.get('detected_contents', []), + 'plugins': manifest.get('plugins', []), + 'total_uncompressed': manifest.get('total_uncompressed'), + 'file_count': manifest.get('file_count'), + } + return jsonify({'status': 'success', 'data': safe_manifest}) except Exception as e: logger.error("backup_validate failed: %s", e, exc_info=True) return jsonify({'status': 'error', 'message': 'An internal error occurred; see logs for details'}), 500