Enthält: - rdp_client.py: RDP Client mit GUI und Monitor-Auswahl - rdp.sh: Bash-basierter RDP Client - teamleader_test/: Network Scanner Fullstack-App - teamleader_test2/: Network Mapper CLI Subdirectories mit eigenem Repo wurden ausgeschlossen. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
326 lines
9.0 KiB
Markdown
326 lines
9.0 KiB
Markdown
# CRITICAL FIXES - Quick Reference
|
|
|
|
## 🔴 BLOCKERS THAT PREVENT THE TOOL FROM WORKING
|
|
|
|
### 1. Frontend Dependencies Missing
|
|
```bash
|
|
cd frontend
|
|
npm install
|
|
```
|
|
**Why**: 537 TypeScript errors preventing compilation
|
|
|
|
---
|
|
|
|
### 2. Frontend Type Mismatches
|
|
**File**: `frontend/src/types/api.ts`
|
|
|
|
Replace lines 5-46 with:
|
|
```typescript
|
|
export interface Service {
|
|
id: number;
|
|
host_id: number;
|
|
port: number;
|
|
protocol: string;
|
|
service_name: string | null;
|
|
service_version: string | null;
|
|
state: string;
|
|
banner: string | null;
|
|
first_seen: string; // ← MISSING
|
|
last_seen: string; // ← MISSING
|
|
}
|
|
|
|
export interface Host {
|
|
id: number;
|
|
ip_address: string;
|
|
hostname: string | null;
|
|
mac_address: string | null;
|
|
status: 'online' | 'offline' | 'scanning'; // ← WRONG: was 'up' | 'down'
|
|
last_seen: string;
|
|
first_seen: string;
|
|
scan_id: number | null;
|
|
}
|
|
|
|
export interface Scan {
|
|
id: number;
|
|
network_range: string; // ← WRONG: was 'target'
|
|
scan_type: 'quick' | 'standard' | 'deep' | 'custom';
|
|
status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
progress: number;
|
|
hosts_found: number; // ← WRONG: was 'total_hosts'
|
|
ports_scanned: number; // ← WRONG: was 'hosts_scanned'
|
|
started_at: string; // ← WRONG: was 'start_time'
|
|
completed_at: string | null; // ← WRONG: was 'end_time'
|
|
error_message: string | null;
|
|
}
|
|
```
|
|
|
|
**Why**: Frontend will crash at runtime when API returns data
|
|
|
|
---
|
|
|
|
### 3. Database Session Leaks in Background Tasks
|
|
**File**: `app/api/endpoints/scans.py`
|
|
|
|
Replace the `start_scan` function (lines 19-52) with:
|
|
```python
|
|
@router.post("/start", response_model=ScanStartResponse, status_code=202)
|
|
async def start_scan(
|
|
config: ScanConfigRequest,
|
|
background_tasks: BackgroundTasks,
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Start a new network scan."""
|
|
try:
|
|
scan_service = ScanService(db)
|
|
scan = scan_service.create_scan(config)
|
|
|
|
# Schedule background execution with fresh session
|
|
async def run_scan():
|
|
fresh_db = SessionLocal()
|
|
try:
|
|
fresh_service = ScanService(fresh_db)
|
|
await fresh_service.execute_scan(scan.id, config)
|
|
finally:
|
|
fresh_db.close()
|
|
|
|
background_tasks.add_task(run_scan)
|
|
|
|
logger.info(f"Started scan {scan.id} for {config.network_range}")
|
|
|
|
return ScanStartResponse(
|
|
scan_id=scan.id,
|
|
message=f"Scan started for network {config.network_range}",
|
|
status=ScanStatusEnum.PENDING
|
|
)
|
|
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
except Exception as e:
|
|
logger.error(f"Error starting scan: {e}", exc_info=True)
|
|
raise HTTPException(status_code=500, detail="Failed to start scan")
|
|
```
|
|
|
|
**Why**: Current code passes db session that closes before scan executes
|
|
|
|
---
|
|
|
|
### 4. WebSocket Not Connected to Scan Updates
|
|
**File**: `app/services/scan_service.py`
|
|
|
|
Add import at top (line 5):
|
|
```python
|
|
from app.api.endpoints.websocket import broadcast_scan_update
|
|
```
|
|
|
|
Replace the progress callbacks (around lines 302-322) with:
|
|
```python
|
|
def _on_host_progress(
|
|
self,
|
|
scan_id: int,
|
|
host: str,
|
|
progress: float,
|
|
callback: Optional[callable]
|
|
) -> None:
|
|
"""Handle host discovery progress."""
|
|
# Broadcast via WebSocket
|
|
asyncio.run_coroutine_threadsafe(
|
|
broadcast_scan_update(scan_id, 'scan_progress', {
|
|
'progress': progress * 0.5,
|
|
'current_host': host
|
|
}),
|
|
asyncio.get_event_loop()
|
|
)
|
|
|
|
def _on_port_progress(
|
|
self,
|
|
scan_id: int,
|
|
host: str,
|
|
port: int,
|
|
progress: float,
|
|
callback: Optional[callable]
|
|
) -> None:
|
|
"""Handle port scanning progress."""
|
|
asyncio.run_coroutine_threadsafe(
|
|
broadcast_scan_update(scan_id, 'scan_progress', {
|
|
'progress': 0.5 + (progress * 0.5),
|
|
'current_host': host,
|
|
'current_port': port
|
|
}),
|
|
asyncio.get_event_loop()
|
|
)
|
|
```
|
|
|
|
**Why**: Users won't see real-time scan progress
|
|
|
|
---
|
|
|
|
### 5. WebSocket Thread Safety Issue
|
|
**File**: `app/api/endpoints/websocket.py`
|
|
|
|
Replace the `ConnectionManager` class (lines 8-56) with:
|
|
```python
|
|
class ConnectionManager:
|
|
"""Manager for WebSocket connections."""
|
|
|
|
def __init__(self):
|
|
"""Initialize connection manager."""
|
|
self.active_connections: Set[WebSocket] = set()
|
|
self.lock = asyncio.Lock()
|
|
|
|
async def connect(self, websocket: WebSocket):
|
|
"""Accept and register a new WebSocket connection."""
|
|
await websocket.accept()
|
|
async with self.lock:
|
|
self.active_connections.add(websocket)
|
|
logger.info(f"WebSocket connected. Total: {len(self.active_connections)}")
|
|
|
|
def disconnect(self, websocket: WebSocket):
|
|
"""Remove a WebSocket connection."""
|
|
self.active_connections.discard(websocket)
|
|
logger.info(f"WebSocket disconnected. Total: {len(self.active_connections)}")
|
|
|
|
async def send_personal_message(self, message: dict, websocket: WebSocket):
|
|
"""Send message to specific WebSocket."""
|
|
try:
|
|
await websocket.send_json(message)
|
|
except Exception as e:
|
|
logger.error(f"Error sending message: {e}")
|
|
self.disconnect(websocket)
|
|
|
|
async def broadcast(self, message: dict):
|
|
"""Broadcast message to all connected WebSockets."""
|
|
disconnected = set()
|
|
|
|
# Make a copy under lock
|
|
async with self.lock:
|
|
connections_copy = self.active_connections.copy()
|
|
|
|
for connection in connections_copy:
|
|
try:
|
|
await connection.send_json(message)
|
|
except Exception as e:
|
|
logger.error(f"Error broadcasting: {e}")
|
|
disconnected.add(connection)
|
|
|
|
# Clean up disconnected
|
|
for connection in disconnected:
|
|
self.disconnect(connection)
|
|
```
|
|
|
|
**Why**: Race conditions can lose connections or cause crashes
|
|
|
|
---
|
|
|
|
### 6. Frontend Environment Variables
|
|
**Create file**: `frontend/.env.example`
|
|
```env
|
|
VITE_API_URL=http://localhost:8000
|
|
VITE_WS_URL=ws://localhost:8000
|
|
```
|
|
|
|
**Create file**: `frontend/.env`
|
|
```env
|
|
VITE_API_URL=http://localhost:8000
|
|
VITE_WS_URL=ws://localhost:8000
|
|
```
|
|
|
|
**Why**: Frontend can't connect to backend without these
|
|
|
|
---
|
|
|
|
### 7. Port Range Validation
|
|
**File**: `app/scanner/port_scanner.py`
|
|
|
|
Replace `parse_port_range` method (lines 128-157) with:
|
|
```python
|
|
def parse_port_range(self, port_range: str) -> List[int]:
|
|
"""Parse port range string to list of ports."""
|
|
ports = set()
|
|
|
|
try:
|
|
for part in port_range.split(','):
|
|
part = part.strip()
|
|
|
|
if not part:
|
|
continue
|
|
|
|
try:
|
|
if '-' in part:
|
|
# Range like "8000-8100"
|
|
parts = part.split('-')
|
|
if len(parts) != 2:
|
|
logger.error(f"Invalid range format: {part}")
|
|
continue
|
|
|
|
start, end = int(parts[0].strip()), int(parts[1].strip())
|
|
if not (1 <= start <= end <= 65535):
|
|
logger.error(f"Port range out of bounds: {start}-{end}")
|
|
continue
|
|
|
|
ports.update(range(start, end + 1))
|
|
else:
|
|
# Single port
|
|
port = int(part)
|
|
if not (1 <= port <= 65535):
|
|
logger.error(f"Port out of range: {port}")
|
|
continue
|
|
ports.add(port)
|
|
|
|
except ValueError as e:
|
|
logger.error(f"Invalid port specification: {part}")
|
|
continue
|
|
|
|
return sorted(list(ports))
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error parsing port range '{port_range}': {e}")
|
|
return []
|
|
```
|
|
|
|
**Why**: Invalid port ranges cause uncaught exceptions
|
|
|
|
---
|
|
|
|
### 8. Search Input Validation
|
|
**File**: `app/api/endpoints/hosts.py`
|
|
|
|
Update line 20:
|
|
```python
|
|
search: Optional[str] = Query(None, max_length=100, description="Search by IP or hostname"),
|
|
```
|
|
|
|
**Why**: Prevents DoS with huge search strings
|
|
|
|
---
|
|
|
|
## Testing Verification
|
|
|
|
Run these to verify fixes work:
|
|
|
|
```bash
|
|
# Backend
|
|
python -c "from app.database import init_db; init_db(); print('✅ DB OK')"
|
|
python -c "from app.api.endpoints.websocket import manager; print('✅ WebSocket OK')"
|
|
|
|
# Frontend
|
|
cd frontend && npm install && npm run build
|
|
# Should complete without errors
|
|
```
|
|
|
|
---
|
|
|
|
## Deploy Checklist After Fixes
|
|
|
|
- [ ] Backend starts without errors: `python main.py`
|
|
- [ ] Frontend builds: `cd frontend && npm run build`
|
|
- [ ] API responds: `curl http://localhost:8000/health`
|
|
- [ ] WebSocket connects: Check browser console
|
|
- [ ] Can start scan via API
|
|
- [ ] Real-time updates in WebSocket
|
|
- [ ] Frontend shows scan progress
|
|
- [ ] Hosts display correctly
|
|
|
|
---
|
|
|
|
**Estimated Time to Fix**: 2-3 hours for experienced developer
|