# Security Audit Report

**Data:** 2026-06-09  
**Repo:** scraper-prawny.klono.ai (`/var/www/html/scraper-prawny.klono.ai/`)  
**Git:** Brak repozytorium git (katalog nie jest pod kontrolą wersji)  
**Visibility:** Serwer produkcyjny — brak git remote, pliki dostępne na filesystem  

---

## TL;DR

Znaleziono **5 hardcoded sekretów** w kodzie źródłowym, z czego **SendGrid API key jest wpisany na sztywno** (bez `os.getenv()`), a 3 kolejne mają prawdziwe wartości jako fallback w `os.getenv()`. Klucz CRM API jest dodatkowo wypisany plaintext w `CLAUDE.md`. Brak `.gitignore`, brak `.env` — sekrety żyją bezpośrednio w kodzie Python. Brak git oznacza, że nie ma historii do czyszczenia, ale też nie ma żadnych zabezpieczeń przed przypadkowym pushowaniem sekretów w przyszłości.

---

## Krok 1 — Znalezione sekrety

### 1.1 Sekrety w kodzie źródłowym

| # | Typ | Plik | Linia | Wartość (zamaskowana) | Mechanizm | Ryzyko |
|---|-----|------|-------|-----------------------|-----------|--------|
| 1 | SendGrid API Key | `scraper/config.py` | 59 | `SG.r3dzD...LaWs` | **Hardcoded string** — brak `os.getenv()` | KRYTYCZNE — klucz jest w pliku wprost |
| 2 | Google Maps API Key | `scraper/config.py` | 54 | `AIzaS...J-7g` | `os.getenv()` z fallbackiem na prawdziwy klucz | WYSOKIE — jeśli env var nie ustawiona, używa hardcoded |
| 3 | Apify API Key | `scraper/config.py` | 55 | `apify_api_zch8a...wdYo` | `os.getenv()` z fallbackiem na prawdziwy klucz | WYSOKIE |
| 4 | CRM API Key (dgred) | `scraper/config.py` | 18 | `87e66...bb532` | `os.getenv()` z fallbackiem na prawdziwy klucz | WYSOKIE |
| 5 | CRM API Key (dgred) | `CLAUDE.md` | 23 | `87e66...bb532` | Plaintext w dokumentacji | ŚREDNIE (plik instrukcji, ale zawiera prawdziwy klucz) |

### 1.2 Inne wrażliwe dane

| # | Typ | Plik | Linia | Opis |
|---|-----|------|-------|------|
| 6 | Email testowy (PII) | `scraper/config.py` | 14 | `dakuchur@gmail.com` — email osobisty jako TEST_EMAIL |
| 7 | Email alertowy | `scraper/config.py` | 58 | `d@dgx.dev` — ALERT_EMAIL |

### 1.3 Pliki konfiguracyjne — brakujące zabezpieczenia

- **Brak `.env` / `.env.local`** — sekrety nie są w osobnym pliku env, lecz hardcoded w `config.py`
- **Brak `.gitignore`** — nawet gdyby dodano `.env`, nie byłby chroniony
- **Brak `.env.example`** — brak szablonu zmiennych środowiskowych

### 1.4 Historia git

Katalog **nie jest repozytorium git** — brak `.git/`. Oznacza to:
- Nie ma historii commitów do przeszukania
- Nie ma ryzyka wycieku przez historię git
- Ale też nie ma żadnych zabezpieczeń (pre-commit hooks, .gitignore)

> **Uwaga:** Nie użyto `gitleaks` ani `trufflehog` — brak git czyni je bezprzedmiotowymi.

### 1.5 Logi

Pliki `scraper/logs/scraper.log` i `scraper/logs/cron.log` **nie zawierają sekretów API** — logują jedynie nazwy firm, emaile i statusy operacji. To dobrze.

---

## Krok 2 — Jak sekret wyciekł (analiza mechanizmu)

### 2a. Główna przyczyna: hardcoded secrets w `config.py`

Klucz SendGrid (`config.py:59`) jest **wpisany na sztywno jako string literal**:
```python
SENDGRID_API_KEY = "SG.r3dzD...LaWs"
```
Nie korzysta z `os.getenv()` — nawet jeśli zmienna środowiskowa byłaby ustawiona, kod ją ignoruje.

Trzy pozostałe klucze (Google Maps, Apify, CRM) używają `os.getenv()`, ale z **fallbackiem na prawdziwe klucze**:
```python
GOOGLE_MAPS_API_KEY = os.getenv("GOOGLE_MAPS_API_KEY", "AIzaSyBINd2jsc...")
```
To oznacza, że klucz jest w kodzie źródłowym nawet jeśli env var jest ustawiona — fallback to prawdziwa wartość.

### 2b. CLAUDE.md jako dodatkowy wektor

Plik `CLAUDE.md` zawiera CRM API key (`87e66...bb532`) w sekcji "CRM — dane dostępowe". Plik ten jest instrukcją dla Claude Code, ale jest czytelny dla każdego z dostępem do serwera. Jeśli repo trafi do gita — ten klucz będzie w historii.

### 2c. Brak `.env` jako praktyki

Projekt nie korzysta z pliku `.env` mimo że `python-dotenv` jest w `requirements.txt`. Sekretami zarządza się bezpośrednio w `config.py` — **każdy kto widzi kod źródłowy, widzi wszystkie klucze**.

### 2d. Scenariusz wycieku SendGrid

Najbardziej prawdopodobna ścieżka wycieku:
1. Klucz SendGrid wpisano na sztywno w `config.py:59` (jedyny sekret BEZ `os.getenv()`)
2. Pliki serwera są dostępne dla osób z dostępem SSH/FTP do `/var/www/html/`
3. Jeśli kod był kiedykolwiek skopiowany, wysłany mailem, wrzucony na git — klucz wyciekł razem z nim
4. Brak jakichkolwiek mechanizmów obronnych (`.gitignore`, pre-commit, secret scanning)

### 2e. Wycieki przez logi/telemetrię

**Nie znaleziono** logowania sekretów w kodzie. Logi zawierają jedynie dane operacyjne (nazwy firm, emaile leadów, statusy HTTP). **To nie jest wektor wycieku.**

### 2f. Frontend / klient

**Nie dotyczy** — projekt to backend scraper w Pythonie, nie ma frontendu. Klucze nie trafiają do bundla JS.

### 2g. CI/CD

**Nie znaleziono** plików CI/CD (`.github/workflows/`, `.gitlab-ci.yml`, itp.). Brak tego wektora.

---

## Krok 3 — Rekomendacje

### PILNE (do zrobienia dziś)

- [ ] **Rotacja WSZYSTKICH kluczy** — nie tylko SendGrid. Google Maps, Apify i CRM dgred mają prawdziwe klucze w kodzie:
  - SendGrid: unieważniony (wg user) — wygeneruj nowy z ograniczonym scope (tylko `Mail Send`)
  - Google Maps: wygeneruj nowy klucz z ograniczeniem do Places API + IP allowlist
  - Apify: wygeneruj nowy token
  - CRM dgred: zmień API key (jeśli możliwe)
- [ ] **Usunąć hardcoded klucze z `config.py`** — zastąpić `os.getenv()` BEZ fallbacku na prawdziwe wartości
- [ ] **Utworzyć plik `.env`** z prawdziwymi wartościami (nie trackowany przez git)
- [ ] **Utworzyć `.env.example`** z pustymi wartościami (szablon)
- [ ] **Usunąć klucz CRM z `CLAUDE.md`** — zastąpić placeholderem

### ŚREDNI PRIORYTET (tydzień)

- [ ] **Zainicjalizować git** w projekcie z prawidłowym `.gitignore` od startu
- [ ] **Dodać `.gitignore`** z wpisami: `.env`, `.env.*`, `!.env.example`, `*.pem`, `*.key`, `__pycache__/`, `*.pyc`, `scraper/logs/*.log`
- [ ] **Zainstalować pre-commit + gitleaks** jako hook blokujący commity z sekretami
- [ ] **SendGrid — ograniczyć scope** nowego klucza do `Mail Send` only
- [ ] **Google Maps — dodać IP allowlist** jeśli API wywoływane tylko z tego serwera

### DŁUGOTERMINOWE

- [ ] Rozważyć secret manager (np. Doppler, 1Password Secrets Automation) zamiast `.env` na serwerze
- [ ] Ustawić rotację kluczy co 90 dni w kalendarzu
- [ ] Dodać CI/CD z `gitleaks` skanowaniem na push/PR gdy repo trafi na GitHub/GitLab

---

## Co zrobiłem automatycznie

Nic — raport jest czysto diagnostyczny. Żadne pliki nie zostały zmienione.

## Czego NIE zrobiłem (czeka na decyzję)

1. **Nie zmieniałem `config.py`** — wymaga decyzji o rotacji kluczy
2. **Nie tworzył `.env` / `.gitignore`** — wymaga potwierdzenia
3. **Nie inicjalizowałem git** — wymaga decyzji o remote/hosting
4. **Nie usuwałem klucza z `CLAUDE.md`** — wymaga decyzji czy zachować inną strukturę instrukcji
5. **Nie instalowałem gitleaks/pre-commit** — wymaga potwierdzenia

---

*Raport wygenerowany przez Claude Code, 2026-06-09*
