maziggy 3 месяцев назад
Родитель
Сommit
d713577863
4 измененных файлов с 393 добавлено и 10 удалено
  1. 1 1
      CHANGELOG.md
  2. 318 0
      RELEASE_NOTES_0.1.6.md
  3. 57 5
      backend/app/services/external_camera.py
  4. 17 4
      backend/app/services/github_backup.py

+ 1 - 1
CHANGELOG.md

@@ -2,7 +2,7 @@
 
 
 All notable changes to Bambuddy will be documented in this file.
 All notable changes to Bambuddy will be documented in this file.
 
 
-## [0.1.6-final] - Not released
+## [0.1.6-final] - 2026-01-31
 
 
 ### New Features
 ### New Features
 - **Group-Based Permissions** - Granular access control with user groups:
 - **Group-Based Permissions** - Granular access control with user groups:

+ 318 - 0
RELEASE_NOTES_0.1.6.md

@@ -0,0 +1,318 @@
+# Bambuddy v0.1.6 - Final Release
+
+**Release Date:** January 31, 2026
+
+After 11 beta releases and extensive community testing, we're excited to announce **Bambuddy 0.1.6-final** - our biggest release yet! This release brings optional authentication, build plate detection, external camera support, model-based queue scheduling, and much more.
+
+---
+
+## Highlights
+
+### Optional Authentication & User Management
+Secure your Bambuddy instance for the first time:
+- Enable/disable authentication via Settings
+- **Role-based access**: Admin (full access) and User (prints only) roles
+- **Group-based permissions**: 50+ granular permissions with custom groups
+- JWT-based authentication with user management UI
+- Users can change their own password from the sidebar
+
+### Build Plate Empty Detection
+Never start a print with objects on the bed again:
+- Per-printer toggle for plate detection
+- Multi-reference calibration (up to 5 plate types)
+- Automatic print pause when objects detected
+- Push notifications and WebSocket alerts
+- ROI calibration for precise detection area
+
+### External & USB Camera Support
+Use any camera with your printers:
+- **External cameras**: MJPEG, RTSP, HTTP snapshot support
+- **USB cameras**: V4L2 webcam support on Linux
+- Layer-based timelapse with external cameras
+- Finish photo capture from external sources
+
+### Model-Based Queue Assignment
+Perfect for print farms:
+- Queue items to "Any X1C", "Any P1S", etc.
+- Auto-assigns to available printer when ready
+- Automatic filament validation and AMS mapping
+- Matches required filaments to loaded spools
+
+### GitHub Profile Backup
+Automated backup of your settings to GitHub:
+- Schedule hourly, daily, or weekly backups
+- Backs up K-profiles, cloud profiles, and app settings
+- Skip unchanged commits (only commit when data changes)
+- Backup history log with commit links
+
+### Prometheus Metrics
+Export printer telemetry for external monitoring:
+- Endpoint: `GET /api/v1/metrics`
+- Printer temps, fans, WiFi, print progress
+- Ready for Grafana dashboards
+- Optional bearer token authentication
+
+---
+
+## New Features
+
+### Authentication & Security
+- **Optional Authentication** - Secure your Bambuddy instance with JWT-based user authentication
+- **Group-Based Permissions** - 50+ granular permissions with custom groups (Administrators, Operators, Viewers)
+- **Change Password** - Users can update their own password from sidebar
+- **API Keys** - API key authentication with granular permissions
+
+### Virtual Printer
+- **Virtual Printer** - Emulates a Bambu Lab printer on your network for Bambu Studio/Orca Slicer
+- **Virtual Printer Queue Mode** - Auto-archive and queue prints from slicer
+- **Virtual Printer Model Selection** - Choose which printer model to emulate
+- **TLS 1.3 Encryption** - Secure MQTT + FTPS with auto-generated certificates
+
+### Print Queue & Scheduling
+- **Model-Based Queue Assignment** - Queue to "Any X1C", "Any P1S" with auto filament matching
+- **Multi-Printer Selection** - Send prints to multiple printers at once
+- **Per-Printer AMS Mapping** - Configure filament mapping individually per printer
+- **Queue Bulk Edit** - Select and edit multiple queue items at once
+- **Queue Only Mode** - Stage prints without auto-start, release when ready
+- **Unassigned Queue Items** - Queue items without assigned printer
+- **Add to Queue from File Manager** - Queue sliced files directly from library
+- **Print Queue Plate Selection** - Full print configuration in queue modal
+- **Deferred Archive Creation** - Archives created when prints start, not when queued
+
+### Smart Plugs & Automation
+- **MQTT Smart Plug Support** - Monitor energy from Zigbee2MQTT, Shelly, Tasmota
+- **Home Assistant Integration** - Control any HA switch/light as a smart plug
+- **HA Energy Sensors** - Use separate sensor entities for power monitoring
+- **Tasmota Discovery** - Auto-discover Tasmota devices on network
+- **Switchbar Widget** - Quick power toggle in sidebar
+- **Tasmota Admin Link** - Quick access to plug web interface
+
+### Camera & Streaming
+- **External Camera Support** - MJPEG, RTSP, HTTP snapshot cameras
+- **USB Camera Support** - V4L2 webcam support on Linux
+- **Build Plate Empty Detection** - AI-powered detection with multi-reference calibration
+- **OBS Streaming Overlay** - Embeddable page at `/overlay/:printerId`
+- **Camera Zoom & Fullscreen** - 100%-400% zoom with pan support
+- **Multiple Embedded Viewers** - Open multiple camera streams simultaneously
+- **Camera View Mode** - Choose between new window or embedded overlay
+- **Layer-Based Timelapse** - External camera timelapse on layer change
+- **Finish Photo in Notifications** - `{finish_photo_url}` template variable
+
+### File Manager
+- **STL Thumbnail Generation** - Auto-generate 3D previews for STL files
+- **ZIP File Support** - Upload and extract ZIP files directly
+- **Create Folder from ZIP** - Auto-create folder named after ZIP file
+- **File Manager Sorting** - Sort by name, size, or date
+- **File Manager Rename** - Rename files and folders directly
+- **File Manager Print Button** - Print directly from selection toolbar
+- **Resizable Sidebar** - Drag to adjust width (200-500px)
+- **Text Wrap Toggle** - Wrap long folder names instead of truncating
+- **Mobile Accessibility** - Touch-friendly with always-visible menus
+
+### Archives & Projects
+- **Multi-Plate Selection** - Select which plate to print from multi-plate 3MF
+- **Archive Plate Browsing** - Navigate plate thumbnails in archive cards
+- **External Links** - Link archives to Printables, Thingiverse, etc.
+- **Fusion 360 Attachments** - Attach F3D design files to archives
+- **Project Import/Export** - Export/import projects as ZIP with all files
+- **BOM Item Editing** - Edit Bill of Materials items after creation
+- **Bulk Project Assignment** - Assign multiple archives to project at once
+- **Project Parts Tracking** - Track parts separately from plates
+- **Tag Management** - Create, edit, and apply tags to archives
+- **Archive Comparison** - Compare 2-5 archives side-by-side
+- **AMS Filament Preview** - Preview filament colors in archive cards
+
+### Printer Controls
+- **Printer Controls** - Stop and Pause/Resume buttons with confirmation
+- **Skip Objects** - Skip individual objects without canceling print
+- **Chamber Light Control** - Light toggle button on printer cards
+- **Resizable Printer Cards** - Four sizes (S/M/L/XL)
+- **H2D Pro Support** - Full support for H2D Pro printer model
+
+### AMS & Filament
+- **AMS Color Mapping** - Manual slot selection with auto-matching
+- **Expandable Color Picker** - 32 colors in configurable palette
+- **AMS Slot RFID Re-read** - Re-read filament info via hover menu
+- **Print Options in Modals** - Bed leveling, flow cal, vibration cal, timelapse toggles
+
+### Backup & Monitoring
+- **GitHub Profile Backup** - Scheduled backup to GitHub repository
+- **Prometheus Metrics** - Export telemetry for Grafana
+- **MQTT Publishing** - Publish events to external MQTT brokers
+- **Application Log Viewer** - Real-time log viewing with filters
+- **Support Bundle** - Debug logging with ZIP generation
+- **Comprehensive Backup/Restore** - All settings, users, groups included
+
+### Notifications
+- **HMS Error Notifications** - 853 error codes translated to human-readable messages
+- **Plate Not Empty Notification** - Dedicated category for plate detection
+- **Daily Digest** - Consolidated daily notification summary
+- **Notification Templates** - Customizable message templates
+- **Slack/Mattermost Format** - Proper payload format support
+
+### Statistics & Dashboard
+- **Failure Analysis Widget** - Failure rate with correlations and trends
+- **Statistics Improvements** - Size-aware responsive widgets
+- **Recalculate Costs** - Button to recalculate all archive costs
+- **Time Format Setting** - Configurable date/time format
+- **Print Quantity Tracking** - Track items per print for progress
+
+### Other Improvements
+- **Firmware Update Helper** - Check versions against Bambu Lab servers
+- **Disable Firmware Checks** - Toggle to prevent update checks
+- **Printer Discovery** - Docker subnet scanning, model mapping
+- **FTP Reliability** - Configurable retry with SSL fixes
+- **Pre-built Docker Images** - Pull from GitHub Container Registry
+- **One-Shot Install Scripts** - Simple `curl | bash` installation
+- **Mobile PWA** - Full mobile support with touch gestures
+- **Timelapse Editor** - Trim, speed adjustment, music overlay
+- **Sidebar Badge Indicators** - Queue and upload counts
+
+---
+
+## Bug Fixes
+
+### Print Queue & Scheduling
+- **Home Assistant Auto-On for Queued Prints** - Fixed smart plug not turning on for queue-started prints (Issue #200)
+- **AMS Mapping for Model-Based Queue** - Fixed "Any [Model]" queue jobs failing at filament loading (Issue #192)
+- **Queue prints on A1** - Fixed "MicroSD Card read/write exception error" when starting prints from queue
+- **Multi-Plate Queue Thumbnails** - Queue now shows correct plate thumbnail (Issue #166)
+- **Queue items with library files** - Fixed 500 errors when listing/updating queue items from File Manager
+
+### Printer Status & Display
+- **A1/A1 Mini Status Display** - Fixed incorrect "Printing" status when idle (Issue #168)
+- **Chamber temp on A1/P1S** - Fixed regression where chamber temperature appeared on printers without sensors
+- **Active AMS slot display** - Fixed for H2D printers with multiple AMS units
+- **Printer hour counter** - Fixed not incrementing during prints and inconsistency between views
+
+### AMS & Filament
+- **Empty AMS Slot Recognition** - Fixed removed spools still appearing in Bambuddy (Issue #147)
+- **Spoolman Sync for Transparent Spools** - Fixed sync failures for natural/transparent filaments (Issue #190)
+- **Spoolman tag field** - Now auto-created on first connect, fixing fresh installs (Issue #123)
+- **Spoolman 400 Bad Request** - Fixed when creating spools
+- **AMS filament matching** - Fixed in reprint modal
+- **User preset AMS configuration** - Fixed user presets showing empty fields in Bambu Studio
+
+### Notifications & Webhooks
+- **Progress Milestone Notifications** - Fixed showing wrong time (e.g., "17m" instead of "17h 47m") (Issue #157)
+- **Mattermost/Slack Webhooks** - Added proper payload format support (Issue #133)
+- **Telegram Notification Parsing** - Fixed markdown errors with underscores in error codes
+- **HMS Error Notifications** - 853 error codes now translated to human-readable messages
+- **Notifications sent when printer offline** - Fixed
+
+### Camera & Streaming
+- **Camera stream reconnection** - Automatic recovery from stalled streams
+- **Camera zoom & pan** - Fixed pan range and added pinch-to-zoom for mobile (Issue #132)
+- **P2S/X1E/H2 completion photo** - Fixed internal model codes not recognized (Issue #127)
+- **Browser freeze** - Fixed on print completion when camera stream was open
+- **ffmpeg processes** - Fixed not being killed when closing webcam window
+
+### File Manager & Archives
+- **P2S Empty Archive Tiles** - Fixed FTP search for printers without SD card (Issue #146)
+- **File Manager folder navigation** - Fixed folder opening then jumping back to root (Issue #160)
+- **File Manager upload** - Now accepts all file types, not just ZIP
+- **Multi-plate 3MF metadata** - Single-plate exports now show correct thumbnail
+- **Archive card cache** - Fixed wrong cover image bug
+- **Archive delete safety** - Added checks to prevent deleting parent directories
+
+### Statistics & Tracking
+- **Print time stats** - Now uses actual elapsed time instead of slicer estimates (Issue #137)
+- **Filament cost** - Now uses "Default filament cost" setting instead of hardcoded €25 (Issue #120)
+- **Reprint cost tracking** - Now adds cost to existing total instead of replacing
+- **K-Profiles backup status** - Fixed showing incorrect printer connection count
+
+### Smart Plugs
+- **HA Energy Sensors** - Fixed sensors with lowercase units (w, kwh) not detected (Issue #119)
+
+### UI & UX
+- **Skip objects modal overflow** - Fixed modal going above browser window (Issue #134)
+- **Project card filament badges** - Fixed showing duplicates and raw color codes
+- **Subnet scan serial number** - Fixed A1 Mini showing "unknown-*" placeholder (Issue #140)
+- **Slicer protocol** - Fixed OS detection (Windows vs macOS/Linux)
+
+### API & Backend
+- **Settings API PATCH Method** - Added for Home Assistant rest_command compatibility (Issue #152)
+- **GitHub Backup Timestamps** - Removed volatile timestamps for cleaner git diffs
+- **Plate Calibration Persistence** - Fixed reference images not persisting in Docker
+- **Update module** - Fixed for Docker-based installations
+
+---
+
+## Maintenance
+
+- Upgraded vitest from 2.x to 3.x for security improvements
+- Added security scanning (pip-audit, npm audit) to CI pipeline
+- Replaced python-jose with PyJWT to eliminate ecdsa vulnerability
+- Improved test coverage (796 backend tests, 518 frontend tests)
+
+---
+
+## Thank You!
+
+This release wouldn't be possible without our amazing community. A huge thank you to everyone who contributed code, reported bugs, tested beta releases, and provided feedback!
+
+### Code Contributors
+
+| Contributor | Contribution |
+|-------------|--------------|
+| **[@maziggy](https://github.com/maziggy)** (MartinNYHC) | Lead developer, core features |
+| **[@MisterBeardy](https://github.com/MisterBeardy)** (Wesley Reaves) | STL thumbnail generation |
+| **[@JesseFPV](https://github.com/JesseFPV)** (Jesse Hulswit) | Optional authentication system |
+
+### Issue Reporters & Testers
+
+Special thanks to everyone who reported issues, tested beta releases, and provided valuable feedback:
+
+- **[@Locxion](https://github.com/Locxion)** (Markus Bender) - A1 Mini status bug, log viewer feature
+- **[@cadtoolbox](https://github.com/cadtoolbox)** (Thomas Rambach) - H2D Pro support, model-based queue
+- **[@Twilek-de](https://github.com/Twilek-de)** - Empty AMS slots, Mattermost webhooks, progress milestones
+- **[@elit3ge](https://github.com/elit3ge)** - Archive tiles, completion photos, upload improvements
+- **[@opensourcefan](https://github.com/opensourcefan)** - Subnet scan serial, status colors
+- **[@1nv4lidus3r](https://github.com/1nv4lidus3r)** - Print time stats, skip objects modal
+- **[@joaorgoncalves](https://github.com/joaorgoncalves)** (João Gonçalves) - Filament cost settings, HA energy sync
+- **[@beardofbeespool](https://github.com/beardofbeespool)** (Morton Likely) - STL thumbnail feature request
+- **[@PeterXQChen](https://github.com/PeterXQChen)** (Peter Chen) - File manager folder navigation
+- **[@Robnex](https://github.com/Robnex)** - External links, external spool sync
+- **[@caco3](https://github.com/caco3)** (CaCO3) - Disable firmware checks
+- **[@sbcrumb](https://github.com/sbcrumb)** - Camera zoom feature
+- **[@fcps3](https://github.com/fcps3)** - Spoolman transparent spool sync
+- **[@LucHeart](https://github.com/LucHeart)** - MQTT connection issues
+- **[@ouihq](https://github.com/ouihq)** (Jonas) - File manager queue bug
+- **[@Schuermi7](https://github.com/Schuermi7)** - AMS mapping, cloud 2FA
+- **[@stubbers](https://github.com/stubbers)** (Joseph Stubberfield) - X1C sync issues
+- **[@nvdmedianl](https://github.com/nvdmedianl)** (Nathan) - Spoolman Bambu spool errors
+- **[@lbeumer-bit](https://github.com/lbeumer-bit)** - Notification photos
+- **[@IROKILLER](https://github.com/IROKILLER)** - Home Assistant automations
+- **[@fgrfn](https://github.com/fgrfn)** (Florian) - Dynamic electricity cost
+- **[@JasonSwindle](https://github.com/JasonSwindle)** (Jason Swindle) - Smart plug text overflow
+- **[@Cassiopeia1980](https://github.com/Cassiopeia1980)** - Connection issues
+
+And many more community members who tested, provided feedback, and helped make Bambuddy better!
+
+---
+
+## Upgrade Notes
+
+### From 0.1.5.x or earlier
+- Database migrations run automatically on startup
+- User authentication is optional and disabled by default
+- Existing installations will continue to work without changes
+
+### From 0.1.6 beta
+- All beta migrations are included in the final release
+- No action required - just update and restart
+
+---
+
+## What's Next?
+
+We're already planning 0.1.7 with more exciting features. Stay tuned and keep the feedback coming!
+
+- [GitHub Issues](https://github.com/MisterBeardy/bambuddy/issues) - Report bugs and request features
+- [GitHub Discussions](https://github.com/MisterBeardy/bambuddy/discussions) - Join the conversation
+
+---
+
+**Happy Printing!** 🎉
+
+*— The Bambuddy Team*

+ 57 - 5
backend/app/services/external_camera.py

@@ -1,6 +1,10 @@
 """External camera service.
 """External camera service.
 
 
 Supports MJPEG streams, RTSP streams (via ffmpeg), HTTP snapshot URLs, and USB cameras.
 Supports MJPEG streams, RTSP streams (via ffmpeg), HTTP snapshot URLs, and USB cameras.
+
+Security Note: This service intentionally makes requests to user-configured camera URLs.
+This is necessary functionality for external camera integration. URLs are validated
+to ensure they are well-formed before use.
 """
 """
 
 
 import asyncio
 import asyncio
@@ -9,12 +13,36 @@ import re
 import shutil
 import shutil
 from collections.abc import AsyncGenerator
 from collections.abc import AsyncGenerator
 from pathlib import Path
 from pathlib import Path
+from urllib.parse import urlparse
 
 
 import aiohttp
 import aiohttp
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
 
 
+def _validate_camera_url(url: str, allowed_schemes: tuple[str, ...] = ("http", "https", "rtsp")) -> bool:
+    """Validate camera URL format.
+
+    This validates that the URL is well-formed and uses an allowed scheme.
+    Note: This intentionally allows user-provided URLs as that is the
+    purpose of external camera configuration.
+
+    Args:
+        url: URL to validate
+        allowed_schemes: Tuple of allowed URL schemes
+
+    Returns:
+        True if URL is valid, False otherwise
+    """
+    try:
+        parsed = urlparse(url)
+        if not parsed.scheme or not parsed.netloc:
+            return False
+        return parsed.scheme.lower() in allowed_schemes
+    except Exception:
+        return False
+
+
 def list_usb_cameras() -> list[dict]:
 def list_usb_cameras() -> list[dict]:
     """List available USB cameras (V4L2 devices on Linux).
     """List available USB cameras (V4L2 devices on Linux).
 
 
@@ -123,12 +151,23 @@ async def _capture_usb_frame(device: str, timeout: int) -> bytes | None:
         logger.error("ffmpeg not found - required for USB camera capture")
         logger.error("ffmpeg not found - required for USB camera capture")
         return None
         return None
 
 
-    # Validate device path
+    # Validate device path - must be /dev/videoN format
     if not device.startswith("/dev/video"):
     if not device.startswith("/dev/video"):
         logger.error(f"Invalid USB device path: {device}")
         logger.error(f"Invalid USB device path: {device}")
         return None
         return None
 
 
-    if not Path(device).exists():
+    # Additional path validation to prevent path traversal
+    # Resolve to absolute path and verify it's still under /dev/
+    try:
+        resolved_path = Path(device).resolve()
+        if not str(resolved_path).startswith("/dev/video"):
+            logger.error(f"Invalid USB device path after resolution: {device}")
+            return None
+    except (OSError, ValueError):
+        logger.error(f"Failed to resolve USB device path: {device}")
+        return None
+
+    if not resolved_path.exists():  # nosec B108 - path validated above
         logger.error(f"USB device does not exist: {device}")
         logger.error(f"USB device does not exist: {device}")
         return None
         return None
 
 
@@ -182,10 +221,15 @@ async def _capture_usb_frame(device: str, timeout: int) -> bytes | None:
 
 
 async def _capture_mjpeg_frame(url: str, timeout: int) -> bytes | None:
 async def _capture_mjpeg_frame(url: str, timeout: int) -> bytes | None:
     """Extract single frame from MJPEG stream."""
     """Extract single frame from MJPEG stream."""
+    # Validate URL format (user-configured camera URL - intentional external request)
+    if not _validate_camera_url(url, ("http", "https")):
+        logger.error(f"Invalid MJPEG URL format: {url[:50]}...")
+        return None
+
     try:
     try:
         async with (
         async with (
             aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout)) as session,
             aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout)) as session,
-            session.get(url) as response,
+            session.get(url) as response,  # nosec B113 - URL validated above, user-configured camera
         ):
         ):
             if response.status != 200:
             if response.status != 200:
                 logger.error(f"MJPEG stream returned status {response.status}")
                 logger.error(f"MJPEG stream returned status {response.status}")
@@ -287,10 +331,15 @@ async def _capture_rtsp_frame(url: str, timeout: int) -> bytes | None:
 
 
 async def _capture_snapshot(url: str, timeout: int) -> bytes | None:
 async def _capture_snapshot(url: str, timeout: int) -> bytes | None:
     """Fetch snapshot from HTTP URL."""
     """Fetch snapshot from HTTP URL."""
+    # Validate URL format (user-configured camera URL - intentional external request)
+    if not _validate_camera_url(url, ("http", "https")):
+        logger.error(f"Invalid snapshot URL format: {url[:50]}...")
+        return None
+
     try:
     try:
         async with (
         async with (
             aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout)) as session,
             aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout)) as session,
-            session.get(url) as response,
+            session.get(url) as response,  # nosec B113 - URL validated above, user-configured camera
         ):
         ):
             if response.status != 200:
             if response.status != 200:
                 logger.error(f"Snapshot URL returned status {response.status}")
                 logger.error(f"Snapshot URL returned status {response.status}")
@@ -348,7 +397,10 @@ async def test_connection(url: str, camera_type: str) -> dict:
             return {"success": False, "error": "Failed to capture frame from camera"}
             return {"success": False, "error": "Failed to capture frame from camera"}
 
 
     except Exception as e:
     except Exception as e:
-        return {"success": False, "error": str(e)}
+        # Sanitize error message - don't expose internal details
+        error_type = type(e).__name__
+        logger.error(f"Camera connection test failed: {e}")
+        return {"success": False, "error": f"Connection failed: {error_type}"}
 
 
 
 
 async def generate_mjpeg_stream(url: str, camera_type: str, fps: int = 10) -> AsyncGenerator[bytes, None]:
 async def generate_mjpeg_stream(url: str, camera_type: str, fps: int = 10) -> AsyncGenerator[bytes, None]:

+ 17 - 4
backend/app/services/github_backup.py

@@ -165,17 +165,30 @@ class GitHubBackupService:
 
 
         except Exception as e:
         except Exception as e:
             logger.error(f"GitHub connection test failed: {e}")
             logger.error(f"GitHub connection test failed: {e}")
-            return {"success": False, "message": str(e), "repo_name": None, "permissions": None}
+            # Sanitize error - don't expose internal details
+            error_type = type(e).__name__
+            return {
+                "success": False,
+                "message": f"Connection failed: {error_type}",
+                "repo_name": None,
+                "permissions": None,
+            }
 
 
     def _parse_repo_url(self, url: str) -> tuple[str, str]:
     def _parse_repo_url(self, url: str) -> tuple[str, str]:
         """Parse owner and repo from GitHub URL."""
         """Parse owner and repo from GitHub URL."""
-        # Handle HTTPS URLs
-        match = re.match(r"https://github\.com/([^/]+)/([^/]+?)(?:\.git)?/?$", url)
+        # Limit URL length to prevent ReDoS attacks
+        if not url or len(url) > 500:
+            raise ValueError("Invalid GitHub URL: URL too long or empty")
+
+        # Handle HTTPS URLs - use atomic groups via limited character classes
+        # GitHub usernames: 1-39 chars, alphanumeric and hyphens
+        # Repo names: 1-100 chars, alphanumeric, hyphens, underscores, dots
+        match = re.match(r"https://github\.com/([\w-]{1,39})/([\w.\-]{1,100})(?:\.git)?/?$", url)
         if match:
         if match:
             return match.group(1), match.group(2)
             return match.group(1), match.group(2)
 
 
         # Handle SSH URLs
         # Handle SSH URLs
-        match = re.match(r"git@github\.com:([^/]+)/([^/]+?)(?:\.git)?$", url)
+        match = re.match(r"git@github\.com:([\w-]{1,39})/([\w.\-]{1,100})(?:\.git)?$", url)
         if match:
         if match:
             return match.group(1), match.group(2)
             return match.group(1), match.group(2)