GitHub Security Advisory: GHSA-9c54-gxh7-ppjc
Package: local-deep-research (PIP)
Severity: MODERATE (CVSS 6.3)
CWE: CWE-918: Server-Side Request Forgery (SSRF)
Vulnerable versions: >= 1.3.0, < 1.3.9
Patched in: 1.3.9

Summary: Local Deep Research is Vulnerable to Server-Side Request Forgery (SSRF) in Download Service

Details:
## Summary

The download service (`download_service.py`) makes HTTP requests using raw `requests.get()` without utilizing the application's SSRF protection (`safe_requests.py`). This can allow attackers to access internal services and attempt to reach cloud provider metadata endpoints (AWS/GCP/Azure), as well as perform internal network reconnaissance, by submitting malicious URLs through the API, depending on the deployment and surrounding controls.

**CWE**: CWE-918 (Server-Side Request Forgery)

---

## Details

### Vulnerable Code Location

**File**: `src/local_deep_research/research_library/services/download_service.py`

The application has proper SSRF protection implemented in `security/safe_requests.py` and `security/ssrf_validator.py`, which blocks:
- Loopback addresses (127.0.0.0/8)
- Private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
- AWS metadata endpoint (169.254.169.254)
- Link-local addresses

However, `download_service.py` bypasses this protection by using raw `requests.get()` directly:

```python
# Line 1038 - _download_generic method
response = requests.get(url, headers=headers, timeout=30)

# Line 1075
response = requests.get(api_url, timeout=10)

# Line 1100
pdf_response = requests.get(pdf_url, headers=headers, timeout=30)

# Line 1144
response = requests.get(europe_url, headers=headers, timeout=30)

# Line 1187
api_response = requests.get(elink_url, params=params, timeout=10)

# Line 1207
summary_response = requests.get(esummary_url, ...)

# Line 1236
response = requests.get(europe_url, headers=headers, timeout=30)

# Line 1276
response = requests.get(url, headers=headers, timeout=10)

# Line 1298
response = requests.get(europe_url, headers=headers, timeout=30)
```

### Attack Vector

1. Attacker submits a malicious URL via `POST /api/resources/<research_id>`
2. URL is stored in database without SSRF validation (`resource_service.py:add_resource()`)
3. Download is triggered via `/library/api/download/<resource_id>`
4. `download_service.py` fetches the URL using raw `requests.get()`, bypassing SSRF protection

---

## PoC

### Prerequisites

- Docker and Docker Compose installed
- Python 3.11+

### Step 1: Create the Mock Internal Service

**File: `internal_service.py`**

```python
#!/usr/bin/env python3
"""Mock internal service that simulates a sensitive internal endpoint."""

from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class InternalServiceHandler(BaseHTTPRequestHandler):
    def log_message(self, format, *args):
        print(f"[INTERNAL SERVICE] {args[0]}")
    
    def do_GET(self):
        print(f"\n{'='*60}")
        print(f"[!] SSRF DETECTED - Internal service accessed!")
        print(f"[!] Path: {self.path}")
        print(f"{'='*60}\n")
        
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        
        secret_data = {
            "status": "SSRF_SUCCESSFUL",
            "message": "You have accessed