## https://sploitus.com/exploit?id=46AEB54A-C764-552D-A4F4-2D26B3E4774F
# Auto Pentest Platform
Platform otomasi penetration testing dengan orkestrator AI agent (LangGraph)
yang HANYA boleh dijalankan terhadap target yang **sudah diotorisasi secara
eksplisit** oleh penggunanya.
> โ ๏ธ **PENTING**: Alat ini menjalankan tool pemindaian/eksploitasi keamanan
> sungguhan (Nmap, Nuclei, sqlmap, Metasploit, dll). Menjalankannya terhadap
> sistem tanpa izin sah adalah ilegal di hampir semua yurisdiksi. Gunakan
> hanya untuk aset milik sendiri atau dengan otorisasi tertulis yang jelas
> (mis. surat penugasan/kontrak pentest).
---
## 1. Arsitektur Singkat
```
input_validation -> recon -> scanning -> vuln_analysis -> human_approval
(hard scope-check) |
+---- rejected/skip ----+
| |
[exploitation] -> reporting -> END
(hanya jika approved)
```
- **Backend**: FastAPI + LangGraph (StateGraph) + Celery (eksekusi async) + SQLite
- **LLM**: Ollama lokal (`qwen2.5:14b`) sebagai default, fallback ke Groq free tier
- **Frontend**: React + Vite + TailwindCSS
- **Guardrail**: validasi scope hard-coded, rate limit & timeout per tool,
audit log lengkap, dan gerbang persetujuan manusia sebelum fase exploitation
- **Tool wrapper**: seluruh tool CLI/API diimplementasikan sebagai class
`BaseTool`/`BaseApiTool` (`backend/tools/base.py`) dengan interface seragam
(`validate_target` -> `run` -> `parse_result` -> `timeout_handler`) dan
output schema seragam (`success/tool/duration/stdout/stderr/findings/raw_output/metadata`)
> ### Catatan perbaikan (Milestone 1.1)
> Versi sebelumnya gagal di-install/login karena dua bug nyata yang sudah
> diperbaiki:
> 1. `requirements.txt` memakai nama paket PyPI yang salah (`zapv2` ->
> seharusnya `python-owasp-zap-v2.4`) dan kombinasi versi
> `langgraph`/`langgraph-checkpoint-sqlite` yang saling tidak kompatibel.
> Sudah diuji ulang: seluruh `requirements.txt` kini resolve bersih.
> 2. `agent/graph.py` memanggil `SqliteSaver.from_conn_string(path)` seolah
> itu instance langsung, padahal di versi langgraph modern method itu
> adalah context-manager generator - akan crash saat runtime. Sudah
> diganti dengan `sqlite3.connect()` + `SqliteSaver(conn)` dan sudah
> diverifikasi `build_graph()` berhasil menghasilkan `CompiledStateGraph`.
> 3. Default `ADMIN_PASSWORD` di `.env.example` sebelumnya `change-me`
> (bukan `admin`) - itulah sebab login `admin`/`admin` gagal kalau `.env`
> belum diedit. Default sekarang `admin`/`admin` untuk uji coba lokal
> (**wajib diganti** sebelum dipakai di luar mesin lokal Anda).
---
## 2. Prasyarat
- Docker & Docker Compose v2
- (Opsional, kalau mau jalan tanpa Docker untuk Ollama) [Ollama](https://ollama.com) terinstal di host
- ~10-15 GB ruang disk kosong (image Ollama + model + tools CLI cukup besar)
- RAM disarankan minimal 16 GB kalau menjalankan model `qwen2.5:14b` secara lokal
---
## 3. Setup dari Nol
### 3.1 Clone & konfigurasi environment
```bash
cd auto-pentest-platform
cp .env.example .env
# edit .env: ganti APP_SECRET_KEY, ADMIN_PASSWORD, ZAP_API_KEY, dst
```
### 3.2 Jalankan seluruh stack
```bash
docker compose up -d --build
```
Proses build pertama kali akan cukup lama karena meng-install banyak tool
CLI (Amass, Nuclei, ffuf, Gobuster, theHarvester, Sublist3r, testssl.sh, dll)
di image backend.
### 3.3 Setup LLM: Groq (default sekarang) atau Ollama (nanti di server)
**Ada 2 cara mengatur API key/provider LLM - pilih salah satu:**
**A) Lewat GUI (lebih praktis, tidak perlu restart container)**
1. Login ke dashboard, klik **โ Settings** di pojok kanan atas.
2. Pilih provider (Groq/Ollama), isi API key/model, klik **Simpan**.
3. Klik **Tes Koneksi** untuk verifikasi langsung dari situ.
Pengaturan ini tersimpan di database (tabel `llm_settings`), jadi berlaku
langsung tanpa perlu edit `.env` atau restart container. Kalau field
dikosongkan, otomatis pakai default dari `.env` (cara B di bawah).
**B) Lewat `.env` (dipakai sebagai default awal / fallback)**
**Default project ini adalah Groq** - API based, gratis (free tier), dan tidak
membutuhkan resource lokal (RAM/GPU) sama sekali. Cocok dipakai selama masih
development di PC pribadi.
1. Daftar & ambil API key gratis di https://console.groq.com/keys
2. Isi di `.env`:
```
LLM_PROVIDER=groq
GROQ_API_KEY=gsk_xxx_isi_dengan_key_anda
```
3. `GROQ_MODEL` sudah di-set default ke `openai/gpt-oss-120b`. **Groq cukup
sering men-deprecate model** (mis. `llama-3.3-70b-versatile` di-deprecate
17 Juni 2026) - kalau suatu saat muncul error `model_decommissioned`, cek
pengganti terbaru di https://console.groq.com/docs/deprecations dan update
`GROQ_MODEL` di `.env` (atau lewat GUI Settings, lebih cepat karena tidak
perlu restart), lalu `docker compose restart backend worker`.
4. Verifikasi cepat tanpa perlu jalankan scan penuh:
```bash
docker exec -it pentest_backend python -c "
from agent.llm import get_llm
from langchain_core.messages import HumanMessage
print(get_llm().invoke([HumanMessage(content='Balas dengan satu kata: OK')]).content)
"
```
Kalau muncul balasan dari model (bukan error), konfigurasi Groq Anda sudah benar.
Ada juga endpoint API untuk tes yang sama: `GET /api/llm/test` (butuh login/Bearer token) -
atau lebih gampang, tombol **Tes Koneksi** di GUI Settings.
**Nanti setelah migrasi ke server yang lebih kuat**, tinggal pindah ke Ollama
lokal (gratis, data tidak keluar server sendiri) lewat GUI Settings (ganti
provider ke "Ollama") atau lewat `.env` tanpa ubah kode apapun:
```bash
docker exec -it pentest_ollama ollama pull qwen2.5:14b
```
lalu di `.env`:
```
LLM_PROVIDER=ollama
OLLAMA_MODEL=qwen2.5:14b
```
Model `qwen2.5:14b` sekitar 9 GB dan butuh RAM cukup besar - kalau server
masih terbatas, mulai dengan model lebih kecil dulu misalnya `qwen2.5:7b`.
### 3.4 Siapkan wordlist untuk fuzzing (ffuf/Gobuster)
```bash
docker exec -it pentest_backend bash -c "mkdir -p /opt/wordlists && curl -sL https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/Web-Content/common.txt -o /opt/wordlists/common.txt"
```
### 3.5 Cek semua service jalan
```bash
docker compose ps
curl http://localhost:8000/health
```
Buka dashboard di **http://localhost:5173**, login dengan
`ADMIN_USERNAME`/`ADMIN_PASSWORD` dari `.env`.
---
## 4. Tools Tambahan (di luar image utama)
Beberapa tool sengaja TIDAK dibundel di image backend karena ukurannya besar
atau butuh service/database sendiri:
### OWASP ZAP
Jalankan sebagai container terpisah dan arahkan `ZAP_API_URL` di `.env` ke situ:
```bash
docker run -u zap -p 8080:8080 -d ghcr.io/zaproxy/zaproxy:stable \
zap.sh -daemon -host 0.0.0.0 -port 8080 -config api.key=
```
### OpenVAS / Greenbone (GVM)
Gunakan image resmi Greenbone Community Edition:
```bash
docker run -d -p 9392:9392 --name gvm greenbone/community-edition
```
Tunggu inisialisasi feed vuln database (bisa >30 menit di jalan pertama).
Tool wrapper `run_openvas()` di `backend/tools/vuln_tools.py` memanggil
`gvm-cli` lewat socket - sesuaikan kredensial di sana atau lewat `.env`.
### Metasploit Framework RPC
```bash
docker run -d -p 55553:55553 --name metasploit metasploitframework/metasploit-framework \
./msfrpcd -P -U msf -a 0.0.0.0 -p 55553
```
Modul exploitation Metasploit **mati secara default**
(`ENABLE_EXPLOITATION_MODULE=false` di `.env`). Aktifkan hanya jika Anda
benar-benar memahami risikonya, dan tetap wajib melalui human approval di UI.
---
## 5. Struktur Folder
```
/backend
/agent -> definisi graph LangGraph, state schema, node-node
/nodes -> satu file per node (input_validation, recon, scanning, ...)
graph.py -> perakitan StateGraph + conditional edges + interrupt
state.py -> PentestState (TypedDict)
llm.py -> factory Ollama/Groq
/tools -> wrapper LangChain Tool per CLI tool
/api -> route FastAPI (auth, scan lifecycle, approval, report)
/models -> ORM SQLite (Scan, Finding, AuditLog)
/core -> config, scope guardrail, audit log, guarded_run (timeout+rate limit)
celery_app.py / tasks.py -> eksekusi graph secara async
/frontend
/src
/components -> ConsentForm, TargetForm, ScanProgress, FindingsTable, ApprovalPanel, ReportViewer
/pages -> Dashboard
/api -> client HTTP + WebSocket
docker-compose.yml
```
---
## 6. Alur Pemakaian
1. Login ke dashboard.
2. Baca & centang **disclaimer/consent form** (dua checkbox persetujuan). Upload dokumen bukti otorisasi TIDAK lagi wajib - cukup attestasi checkbox.
3. Isi form target: domain/IP utama + daftar domain/IP range yang **diotorisasi**
(`authorized_scope`). Scan tidak akan menyentuh apapun di luar daftar ini -
ini divalidasi keras di kode (`backend/core/scope.py`), bukan hanya lewat
instruksi ke LLM.
4. Graph berjalan otomatis: `input_validation -> recon -> scanning -> vuln_analysis`.
Progres tiap node terlihat live di dashboard.
5. Di node **human_approval**, graph berhenti (interrupt) dan menampilkan
ringkasan temuan + rencana next step dari LLM. Klik **Approve** atau
**Reject**.
6. Kalau *Approve* dan Anda mencentang "Izinkan fase exploitation" saat
membuat scan, node `exploitation` akan jalan (sqlmap mode intrusif /
Metasploit check pada temuan yang relevan saja).
7. Node `reporting` menyusun laporan markdown (executive summary, tabel
temuan dengan severity, rekomendasi remediasi). Unduh sebagai PDF dari
dashboard.
Seluruh aksi (tool call, keputusan approve/reject, siapa & kapan) tercatat
di tabel `audit_log`, bisa dilihat lewat endpoint `GET /api/audit-log`.
---
---
## 5a. Live Log, AI Agent Reasoning, dan Kontrol Pause/Stop
Dashboard punya panel tambahan yang terisi otomatis selama scan berjalan:
- **Live Command** - command CLI yang SEDANG dijalankan saat ini (tool + argumen lengkap).
- **Live Output** - output tool mengalir baris-per-baris secara real-time (bukan menunggu tool selesai baru muncul).
- **AI Agent Reasoning** - ringkasan & rencana yang benar-benar dihasilkan LLM di node `vuln_analysis` dan `reporting` (bukan teks statis - hanya muncul saat LLM betulan dipanggil).
- **Temuan Live** - setiap finding baru langsung tercatat begitu tool selesai parsing, tanpa menunggu seluruh scan selesai.
Mekanisme di balik layar:
- `core/process_runner.py` menjalankan tool lewat `Popen` + pembacaan output baris-per-baris (bukan `subprocess.run()` yang blocking sampai selesai), dan mengirim tiap baris ke tabel `scan_events` lewat `core/live_log.py`.
- Timeout & sinyal stop ditegakkan lewat **watchdog thread terpisah** dari pembacaan output - supaya tool yang tidak menghasilkan output sama sekali (mis. macet) tetap bisa dihentikan tepat waktu.
- Frontend polling `GET /api/scans/{id}/live-log?since_id=N` setiap ~1.2 detik (plus WebSocket sebagai trigger tambahan) dan mengakumulasi event secara incremental.
**Tombol Pause / Resume / Stop** (komponen `ScanControls.jsx`):
- **Pause** - menandai `control_signal='pause_requested'`; Celery task berhenti di titik aman berikutnya (antar-node) dan menunggu (polling) sampai di-resume. Status scan berubah jadi `paused`.
- **Resume** - mengembalikan `control_signal='run'`, task melanjutkan dari titik terakhir.
- **Stop** - menandai `control_signal='stop_requested'` **dan** langsung membunuh paksa (`SIGKILL`) proses CLI yang sedang berjalan lewat registry proses aktif (`kill_active_processes()`), sehingga tidak perlu menunggu tool tersebut selesai sendiri. Status scan berubah jadi `stopped` (final, tidak bisa di-resume - buat scan baru kalau ingin mengulang).
Endpoint terkait: `POST /api/scans/{id}/pause`, `/resume`, `/stop`, dan `GET /api/scans/{id}/live-log`.
---
## 5b. CVE Enrichment (deskripsi + CVSS resmi dari NVD)
Setiap temuan yang mengandung CVE ID otomatis diperkaya di akhir node
`vuln_analysis` - tidak perlu setup tambahan, langsung jalan tiap scan.
**Sumber CVE ID** (dua jalur, tidak saling eksklusif):
1. Metadata terstruktur dari tool itu sendiri - Nuclei sering menyertakan
`cve-id` + `cvss-score` langsung di template (`info.classification`).
2. Regex `CVE-YYYY-NNNN` di title/deskripsi/raw output - menjaring temuan
dari Nikto/ZAP/testssl.sh yang menyebut CVE di teks tapi bukan di field
terstruktur.
**Sumber data enrichment:**
- **CVSS**: diprioritaskan dari template Nuclei sendiri (kalau ada), NVD
hanya jadi fallback.
- **Deskripsi resmi**: selalu dari NVD (`GET https://services.nvd.nist.gov/rest/json/cves/2.0`).
**Cache lokal** (tabel `cve_cache`, TTL `CVE_CACHE_TTL_DAYS` hari, default 30)
supaya CVE yang sama tidak berulang kali di-query ke NVD antar-scan - penting
karena rate limit NVD cukup ketat (5 request/30 detik tanpa API key, 50/30
detik dengan API key gratis - isi `NVD_API_KEY` di `.env` untuk naikkan limit).
> **Catatan kebijakan NVD 2026**: sejak 15 April 2026, NIST membatasi
> enrichment CVSS hanya untuk CVE yang masuk kategori CISA KEV / software
> pemerintah federal / "critical software". Banyak CVE lain kemungkinan
> TIDAK punya skor CVSS dari NVD lagi - karena itu CVSS dari template
> Nuclei diprioritaskan sebagai sumber utama.
Kegagalan network/rate-limit ke NVD **tidak pernah menggagalkan scan** -
hanya dicatat sebagai warning, dan finding tetap muncul apa adanya tanpa
enrichment tambahan (fallback ke cache lama kalau ada).
Tampil di dashboard sebagai kolom **CVE / CVSS** di tabel temuan (link
langsung ke halaman NVD) dan ikut masuk ke tabel temuan di laporan akhir.
---
---
## 5c. Multi-Target Paralel
Bisa memasukkan **beberapa target sekaligus** (satu per baris) di form "Scan
Baru" - masing-masing jadi Celery task independen yang benar-benar berjalan
bersamaan, bukan antre satu-satu.
**Yang diaktifkan supaya ini jalan mulus:**
- `docker-compose.yml`: worker default `--concurrency=4` (naik dari 2). Bisa
ditambah lagi dengan `docker compose up -d --scale worker=3` untuk lebih
banyak proses worker paralel (`container_name` worker sengaja tidak
di-fix supaya Compose bisa bikin nama unik per replica).
- `models/db.py`: SQLite diaktifkan **WAL mode** + `busy_timeout=5000` -
tanpa ini, beberapa scan yang menulis findings/audit-log/live-log
bersamaan gampang kena error `database is locked`.
- Endpoint `POST /api/scans` sekarang **mendeteksi (bukan blokir)** kalau
target yang sama sedang aktif di-scan oleh scan lain, dan mengembalikan
`warning` di response - ditampilkan di form. Ini penting karena rate
limiter di `core/process_runner.py` bersifat **per-proses Celery worker**
(bukan terkoordinasi lintas proses), jadi men-scan target yang SAMA dari
2 scan paralel efektif melipatgandakan beban ke target tersebut walau
masing-masing scan sendiri tetap menghormati rate limit-nya.
- Dashboard: badge "N sedang berjalan" + indikator titik warna per status +
auto-refresh daftar riwayat scan tiap 4 detik, supaya progres beberapa
scan yang jalan bersamaan kelihatan live tanpa perlu klik satu-satu.
**Batasan yang perlu diketahui (bukan bug, keputusan desain sadar):**
- Isolasi antar-scan masih di level proses (subprocess), BUKAN container
Docker terpisah per target. Semua tool CLI tetap jalan di dalam container
`worker` yang sama, dibatasi lewat semaphore/rate-limiter internal
(`MAX_CONCURRENT_TOOL_CALLS`), bukan `cgroups`/container per-target.
Isolasi container-per-target (seperti yang dipakai beberapa tool sejenis)
sengaja TIDAK diimplementasikan di sini karena butuh mount
`/var/run/docker.sock` ke container worker - itu sendiri adalah vektor
privilege-escalation yang cukup dikenal (kalau worker ter-compromise,
akses ke docker.sock biasanya setara akses root ke host). Untuk
pemakaian solo/tim kecil, trade-off ini tidak sepadan; kalau nanti perlu
isolasi lebih ketat (multi-tenant sungguhan), pertimbangkan rootless
Docker atau sandbox tingkat OS (gVisor/Kata) alih-alih docker.sock polos.
---
## 6a. Menjalankan Unit Test
```bash
cd backend
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
python -m pytest tests/ -v
```
Atau langsung di dalam container yang sudah jalan:
```bash
docker exec -it pentest_backend python -m pytest tests/ -v
```
Cakupan test saat ini (79 test, `backend/tests/`):
- `test_scope.py` - validator `authorized_scope` (domain, subdomain, CIDR, IP tunggal, exclusion, command-injection guard)
- `test_nmap_parser.py` - parser XML Nmap (`-oX`)
- `test_nuclei_parser.py` - parser JSONL Nuclei
- `test_timeout_handler.py` - `run_subprocess()` + `BaseTool.timeout_handler()`
- `test_retry_handler.py` - retry exponential backoff, dan larangan retry untuk scope-violation/error non-transient/stop
- `test_scan_control.py` - sinyal pause/stop, pembunuhan paksa proses aktif, dan live log
- `test_db_migration.py` - migrasi ringan otomatis (ALTER TABLE ADD COLUMN) untuk DB lama
- `test_llm_settings.py` - override pengaturan LLM lewat GUI, fallback ke `.env`, masking API key
- `test_cve_enrichment.py` - ekstraksi CVE ID, cache NVD, prioritas CVSS template vs NVD, graceful failure saat NVD unreachable
- `test_scanning_hostname_bug.py` - regresi bug hostname vs IP hasil resolve nmap
- `test_consent_not_required.py` - scan tetap bisa jalan tanpa dokumen otorisasi, scope enforcement tetap hard-block
- `test_sqlite_concurrency.py` - WAL mode & busy_timeout aktif (dibutuhkan multi-target paralel)
- `test_scan_overlap_warning.py` - deteksi target yang sedang aktif di-scan scan lain
- `test_scan_control.py` - sinyal pause/stop, pembunuhan paksa proses aktif, dan live log
---
## 7. Guardrail Keamanan (Ringkasan)
| Guardrail | Implementasi |
|---|---|
| Hard scope-check | `core/scope.py` (`assert_in_scope` + `validate_target_format`), dipanggil di `BaseTool.validate_target()` sebelum SETIAP eksekusi tool - bukan hanya di system prompt LLM |
| Rate limit & timeout per kategori | `core/process_runner.py` (sliding-window limiter + semaphore) + `tools/base.py` (`_CATEGORY_TIMEOUTS`: recon 300s, scanning 600s, web 900s) |
| Retry terbatas | `BaseTool._execute_with_retry()` - maksimal 1 retry, exponential backoff, HANYA untuk timeout/error jaringan transient; scope violation & format invalid TIDAK pernah di-retry |
| Audit log lengkap | `core/audit.py` (`write_audit_entry`) - timestamp, user, scan_id, target, tool, arguments, start_time, end_time, duration, exit_code, success, stdout/stderr (terpotong) |
| Human approval sebelum exploitation | `agent/graph.py` (`interrupt_after=["human_approval"]`) + pengecekan berlapis di `agent/nodes/exploitation.py` dan `tools/exploitation_tools.py` |
| Disclaimer & consent | `frontend/src/components/ConsentForm.jsx` - dua checkbox wajib dicentang sebelum bisa membuat scan (`consent_confirmed` di backend). Upload dokumen bukti otorisasi bersifat OPSIONAL (endpoint `/api/consent/upload` masih tersedia kalau ingin dipakai lagi nanti), tidak lagi hard-block di `input_validation` node. |
| Output schema seragam | `tools/base.py` (`ToolOutput.to_dict()`) - semua tool (CLI maupun API) mengembalikan `{success, tool, duration, stdout, stderr, findings, raw_output, metadata}` |
---
## 8. Troubleshooting
- **Ollama lambat/OOM**: pakai model lebih kecil (`qwen2.5:7b`) atau pindah ke `LLM_PROVIDER=groq`.
- **Tool CLI "not found"**: build ulang image backend (`docker compose build backend worker`), cek log build untuk step `go install` yang gagal (butuh koneksi ke proxy Go/GitHub).
- **Graph tidak lanjut setelah approve**: cek log worker (`docker compose logs -f worker`), pastikan checkpoint SQLite (`/data/checkpoints.sqlite`) writable.
- **ZAP/OpenVAS/Metasploit "connection refused"**: pastikan container terpisahnya sudah jalan (lihat bagian 4) dan URL/host di `.env` sudah benar.
---
## 9. Lisensi & Tanggung Jawab
Proyek ini disediakan sebagai kerangka kerja teknis. Pengguna sepenuhnya
bertanggung jawab untuk memastikan setiap penggunaan platform ini sah secara
hukum dan sesuai dengan otorisasi yang dimiliki.
---
## 10. Rekomendasi Milestone 2
Item berikut BELUM production-ready dan disengaja dibiarkan sebagai
placeholder/simplifikasi di MVP ini:
1. **OpenVAS/GVM** - saat ini hanya placeholder (`OpenVASTool`). OpenVAS
bersifat task-based & bisa berjalan lama (menit-jam), tidak cocok
dieksekusi sinkron di dalam satu node LangGraph dengan model timeout
`BaseTool` saat ini. Rekomendasi: jadikan scan OpenVAS sebagai Celery
task terpisah yang di-poll berkala, hasilnya di-merge ke state lewat
node tambahan (mis. `openvas_poll`) sebelum `human_approval`.
2. **sqlmap via API resmi** - MVP ini menjalankan sqlmap CLI dan mem-parsing
file log di `--output-dir` (lebih stabil dari regex stdout, tapi belum
API JSON murni). `sqlmapapi.py` bisa dijalankan sebagai service
terpisah untuk hasil JSON native di Milestone 2.
3. **Gobuster JSON** - Gobuster `dir` mode tidak punya output JSON resmi;
parsing baris regex saat ini cukup stabil tapi tetap best-effort.
Alternatif: pertimbangkan mengganti Gobuster dengan ffuf saja (yang
sudah JSON native) untuk directory brute-force di Milestone 2.
4. **Migrasi skema database** - SQLite di-generate lewat
`Base.metadata.create_all()` (tidak ada Alembic). Aman untuk instalasi
baru, tapi perubahan skema pada instalasi yang sudah berjalan perlu
migrasi manual. Tambahkan Alembic di Milestone 2.
5. **Metasploit RPC** - wrapper `MetasploitCheckTool` baru mendukung mode
`check`. Mode `exploit` sungguhan sengaja tidak disediakan sebagai
fungsi generik karena risikonya tinggi; kalau dibutuhkan, harus
ditambahkan secara sempit per-engagement dengan approval terpisah.
6. **Concurrency guardrail** - rate limiter & semaphore saat ini per-proses
worker (in-memory), belum lintas-worker. Kalau menjalankan >1 replica
Celery worker, pertimbangkan rate limiter berbasis Redis agar batasnya
benar-benar global.
7. **WebSocket live status** - implementasi saat ini polling DB tiap 2
detik per koneksi WebSocket (bukan pub/sub). Cukup untuk skala kecil;
untuk banyak scan paralel, pertimbangkan Redis pub/sub agar tidak
membebani SQLite dengan query berulang.
### Potensi bug yang masih ada
- `MasscanTool.parse_result` menangani output JSON masscan yang terpotong
(proses berhenti di tengah) dengan heuristik `rstrip(",\n")` - ini cukup
untuk kasus umum tapi tidak 100% menjamin JSON valid pada semua pola
kegagalan masscan.
- `theHarvester` dan `Sublist3r` adalah project pihak ketiga yang tidak
selalu aktif di-maintain; kalau sumber OSINT yang dipakai (crt.sh, dst)
berubah format, parsing JSON/list bisa perlu disesuaikan lagi.
- Rate limiter & semaphore concurrency di `core/process_runner.py` bersifat
global per-proses (module-level singleton) - dua scan paralel di worker
yang sama akan berbagi kuota yang sama, ini disengaja sebagai guardrail
tapi bisa membuat scan paralel terasa lambat.