2.6
CVE Intelligence dan RAG
Modul modules/cve_intelligence/ menghasilkan dua artefak terpisah: basis data SQLite data/cve_intelligence.db dan index RAG berupa pickle data/cve_rag_index.pkl. CVEMatcher membaca basis data itu untuk mengayakan temuan dengan related_cves, cve_references, dan cve_links. CVERagIndex membangun ruang vektor TF-IDF di atas korpus CVE dan memakainya untuk mencari CVE yang mirip dengan sebuah temuan. Keduanya hanya aktif ketika konfigurasi menyalakannya, dan keduanya membaca artefak yang sama sekali tidak dibuat saat scan berjalan.
Artefak dan titik pemakaian
graph TD
UPD["scripts/update_cve_database.py"] --> SCR["CVEScraper.scrape_latest_vulnerabilities"]
SCR --> DB[("data/cve_intelligence.db")]
BLD["scripts/build_cve_rag_index.py"] --> BUILD["CVERagIndex.build"]
DB --> BUILD
BUILD --> SAVE["CVERagIndex.save"]
SAVE --> PKL[("data/cve_rag_index.pkl")]
FACT["get_cve_matcher"] --> MATCH["CVEMatcher"]
DB --> MATCH
MATCH --> ENR["enrich_vulnerability"]
ENR --> VULN["temuan: related_cves, cve_references, cve_links"]
PKL --> LOAD["CVERagIndex.load"]
LOAD --> SEARCH["rag.search"]
SEARCH --> RAGV["temuan: cve_references, rag_matched"]
style DB fill:#336791,color:#fff
style PKL fill:#8b5cf6,color:#fff
style ENR fill:#2d7d46,color:#fff
style SEARCH fill:#2d7d46,color:#fff
style FACT fill:#f59e0b,color:#000
style BUILD fill:#f59e0b,color:#000
%% fig: Dari skrip pembangun ke pemakaian saat scan
%% desc: Skrip pembaruan mengisi SQLite, skrip index membaca SQLite lalu menulis pickle, dan saat scan kedua artefak hanya dibaca oleh CVEMatcher dan CVERagIndex.| Artefak | Dibangun oleh | Dibaca oleh | Kunci konfigurasi |
|---|---|---|---|
data/cve_intelligence.db | scripts/update_cve_database.py lewat CVEScraper | CVEMatcher, CVERagIndex._load_cve_rows | experimental.cve_database_path, cve_intelligence.database_path |
data/cve_rag_index.pkl | CVERagIndex.build lalu CVERagIndex.save | CVERagIndex.load | rag.index_path |
Skrip pembangun tidak berbagi state dengan proses scan. scripts/build_cve_rag_index.py memuat konfigurasi lewat ConfigLoader.load, memanggil rag.build(args.cve_db, interactive=True), lalu rag.save() dan mencetak [+] Index saved to ... bersama jumlah CVE yang terindeks (scripts/build_cve_rag_index.py:19-481).
Pembaruan basis data CVE
scripts/update_cve_database.py menyiapkan CVEScraper("data/cve_intelligence.db", use_browser=True) lalu menampilkan statistik awal total_cves, total_exploits, dan total_technologies sebelum menjalankan empat langkah (scripts/update_cve_database.py:84-992).
| Langkah | Fungsi | Sumber | Batas |
|---|---|---|---|
| 1/4 | scrape_latest_vulnerabilities | NVD RSS, CISA KEV | max_items=100 |
| 2/4 | scrape_multi_source | NVD Web, MITRE, Vulners, GitHub | latest_cves[:50] |
| 3/4 | tidak ada, NVD API dinonaktifkan | — | nvd_count = 0 |
| 4/4 | scraper.scrape_exploit_db | Exploit-DB via Playwright | limit=500 |
Langkah 2/4 hanya berjalan bila langkah 1/4 mengembalikan daftar CVE, dan hasil tiap langkah disimpan di scraping_results untuk tabel ringkasan di akhir (scripts/update_cve_database.py:105-1373).
⚠️ Setiap langkah menangkap exception-nya sendiri dan mengembalikan
0, misalnyaconsole.print(f"[red]✗[/red] Error fetching latest vulnerabilities: {e}\n")lalureturn 0, [](scripts/update_cve_database.py:32-554). Kegagalan jaringan karena itu tidak menghentikan skrip dan tidak menghasilkan exit code gagal; periksa tabel ringkasan untuk memastikan ada baris yang benar-benar masuk.
Isi basis data tidak harus lengkap untuk membuat CVEMatcher berguna. ensure_seed_if_empty(min_rows=5) membuat tabel cve_entries, cve_exploits, dan cve_technologies lalu menyisipkan seed bila jumlah baris di bawah batas (modules/cve_intelligence/cve_matcher.py:61-645). Seed itu berisi 16 CVE sinyal tinggi seperti CVE-2021-44228 (Log4Shell) dan CVE-2014-6271 (Shellshock) (modules/cve_intelligence/cve_matcher.py:112-1306), dan id semu CVE-2017-5638-SQL diganti menjadi CVE-2012-2122 sebelum disisipkan (modules/cve_intelligence/cve_matcher.py:296-3037).
Pembangunan index RAG
CVERagIndex.__init__ membaca rag.index_path (default data/cve_rag_index.pkl), rag.top_k (default 5), rag.min_score (default 0.15), dan rag.auto_rebuild (default True) dari blok rag pada konfigurasi (modules/cve_intelligence/rag_index.py:48-598).
| Kunci | Default di kode | Dipakai oleh |
|---|---|---|
rag.index_path | data/cve_rag_index.pkl | load, save, is_stale |
rag.top_k | 5 (engine memakai default 3) | search |
rag.min_score | 0.15 | search |
rag.auto_rebuild | True | is_stale di engine |
rag.enabled | False | gerbang pengayaan RAG di engine |
build mengambil baris lewat _load_cve_rows, yang menjalankan SELECT c.cve_id, c.description, c.cvss_score, c.severity, GROUP_CONCAT(t.technology, '|') dengan LEFT JOIN cve_technologies dan GROUP BY c.cve_id; bila tabel cve_entries tidak ada, fungsi mencatat RAG: cve_entries table missing dan mengembalikan daftar kosong (modules/cve_intelligence/rag_index.py:172-1999). Setiap dokumen adalah gabungan description dan teknologi yang di-join spasi, lalu build mengembalikan False saat DB tidak ditemukan, tabel kosong, atau tidak ada dokumen tersisa (modules/cve_intelligence/rag_index.py:117-13210).
Vektorisasi memakai TfidfVectorizer(stop_words="english", ngram_range=(1, 2), max_features=50000, sublinear_tf=True), dan build mencatat hasilnya sebagai RAG index built: {len(meta)} CVEs, vocab={len(vectorizer.vocabulary_)} (modules/cve_intelligence/rag_index.py:157-17011). save menolak index yang belum dibangun dengan RuntimeError("Cannot save unbuilt index"), lalu menulis payload berisi vectorizer, matrix, cve_meta, built_at, dan doc_count (modules/cve_intelligence/rag_index.py:100-11512).
load mengembalikan False bila berkas pickle tidak ada atau scikit-learn tidak tersedia, dan mencatat RAG: scikit-learn not available; cannot load index (modules/cve_intelligence/rag_index.py:76-8213). Pemeriksaan dependensi itu ada di _ensure_sklearn: tanpa TTY, fungsi langsung mengembalikan False tanpa memasang apa pun; dengan TTY, fungsi menanyakan [!] RAG needs scikit-learn. Install now? [y/N]: lalu menjalankan pip install scikit-learn (modules/cve_intelligence/rag_index.py:18-4514).
search mengembalikan [] bila index belum dimuat atau query kosong, menghitung cosine_similarity terhadap matriks TF-IDF, mengambil k skor tertinggi dengan np.argsort(-scores), membuang hit di bawah min_score, dan menambahkan field score pada setiap hasil (modules/cve_intelligence/rag_index.py:201-22315). is_stale menyatakan index usang bila pickle tidak ada, atau bila st_mtime pickle lebih tua daripada basis data CVE (modules/cve_intelligence/rag_index.py:68-7416).
⚠️ Bila direktori
data/dihapus tetapirag.enabledtetaptrue,builddanloadsama-sama gagal tanpa exception yang keluar:buildmencetakRAG: CVE DB not found at data/cve_intelligence.dbdan pengayaan dilewati tanpa pesan tambahan (modules/cve_intelligence/rag_index.py:123-12517). Pada mode non-interaktif, instalasi scikit-learn tidak akan dipicu sama sekali (modules/cve_intelligence/rag_index.py:26-2818).
Pencocokan temuan dengan CVEMatcher
graph LR
V["dict temuan"] --> T["_search_terms_for_type"]
T --> Q{"search_terms kosong?"}
Q -- tidak --> SQL["cve_entries WHERE description LIKE ... LIMIT 5"]
Q -- ya --> BLOB["gabung type + description"]
BLOB --> T2["_search_terms_for_type(blob)"]
T2 --> SQL
SQL --> REL["related_cves + cve_references + cve_matched"]
REL --> LIVE["_live_enrich"]
LIVE --> LINKS["_attach_links_only"]
style SQL fill:#336791,color:#fff
style REL fill:#2d7d46,color:#fff
style BLOB fill:#f59e0b,color:#000
style LINKS fill:#8b5cf6,color:#fff
%% fig: Jalur enrich_vulnerability dari tipe temuan ke CVE terkait
%% desc: Tipe temuan diterjemahkan menjadi pola LIKE, dicocokkan ke cve_entries dengan batas lima baris, lalu dilengkapi lookup live dan tautan referensi.| Metode | Perilaku |
|---|---|
is_available | menolak berkas yang tidak ada atau berukuran di bawah 100 byte, lalu memastikan SELECT COUNT(*) FROM cve_entries lebih dari 0 (modules/cve_intelligence/cve_matcher.py:42-5919) |
match_technology_cves | LIKE pada technology, description, affected_products, product, dan vendor, urut cvss_score menurun, LIMIT 20 per teknologi, lalu menyaring dengan severity_min (modules/cve_intelligence/cve_matcher.py:372-41020) |
get_payloads_from_cves | memetakan exploit_type ke delapan keranjang payload dan menghapus duplikat dengan dict.fromkeys (modules/cve_intelligence/cve_matcher.py:450-46521) |
_search_terms_for_type | mencari fragmen tipe pada _TYPE_TERMS, dengan fallback token pertama minimal tiga karakter (modules/cve_intelligence/cve_matcher.py:493-50222) |
enrich_vulnerability | query description LIKE ? yang digabung OR, urut cvss_score menurun, LIMIT 5 (modules/cve_intelligence/cve_matcher.py:504-53023) |
_live_enrich | lookup NVD dan GitHub bila live_config["enabled"] (modules/cve_intelligence/cve_matcher.py:571-58224) |
_TYPE_TERMS menjadi kamus kecil antara tipe temuan dan pola SQL, misalnya:
| Fragmen tipe | Pola LIKE |
|---|---|
sql, sqli | %SQL injection%, %SQLi%, %SQL Injection% |
xss, cross-site scripting | %XSS%, %cross-site scripting% |
log4j, log4shell, jndi | %Log4j%, %Log4Shell%, %JNDI% |
enrich_vulnerability menulis related_cves (deskripsi dipotong 150 karakter), menambahkan id baru ke cve_references, lalu menandai cve_matched = True; kegagalan query hanya dicatat pada level debug (modules/cve_intelligence/cve_matcher.py:551-56925). Setelah itu _live_enrich dipanggil dan hasil akhirnya diberi tautan NVD, Exploit-DB, dan GitHub lewat _attach_links_only, yang mengisi cve_links per cve_id (modules/cve_intelligence/cve_matcher.py:648-66326).
Live lookup opsional
sequenceDiagram
participant E as enrich_vulnerability
participant L as _live_enrich
participant N as NVD 2.0 API
participant G as GitHub Search
participant A as _attach_links_only
E->>L: dict temuan
alt live_config.enabled false
L->>A: tautkan referensi saja
else live aktif
L->>N: nvd_get_cve untuk refs[:3]
N-->>L: cve_id, severity, cvss_score, links
L->>N: nvd_search_keyword bila related kosong
N-->>L: hit keyword
L->>G: github_search_repos cve atau keyword
G-->>L: cve_github_pocs
L->>A: related_cves[:8], cve_references[:12]
end
A-->>E: cve_links per cve_id
%% fig: Siklus satu panggilan live lookup
%% desc: enrich_vulnerability memilih jalur tautan saja atau lookup NVD dan GitHub, lalu hasilnya dipangkas dan dilengkapi tautan referensi.Ketiga sumber tautan dibentuk oleh attach_reference_links, yang mengembalikan nvd, exploit_db, dan github_poc untuk id berawalan CVE- (modules/cve_intelligence/live_lookup.py:37-4527). Semua panggilan NVD melewati _throttle dengan jeda minimum 0,6 detik, dan respons disimpan pada _cache per kunci kw: atau id: sehingga pengulangan tidak menembak API lagi (modules/cve_intelligence/live_lookup.py:52-5828). nvd_search_keyword memotong keyword pada 200 karakter, membatasi resultsPerPage antara 1 dan 20, memakai timeout=25, dan mengembalikan daftar kosong untuk status selain 200 (modules/cve_intelligence/live_lookup.py:61-9929).
github_search_repos menyusun query {keyword} exploit OR poc OR vulnerability, mengurutkan berdasarkan bintang, membatasi per_page antara 1 dan 10, dan mengembalikan full_name, stars, url, description, serta language (modules/cve_intelligence/live_lookup.py:147-17530). nvd_get_cve mengembalikan None untuk id yang tidak berawalan CVE- dan menormalkan metrik lewat _normalize_nvd_item (modules/cve_intelligence/live_lookup.py:105-13131). Bila lokal tidak menemukan apa pun, keyword_from_finding memakai CVE id yang tertangkap dari evidence lebih dulu, lalu menyusun keyword dari type dan parameter dengan maksimum enam token (modules/cve_intelligence/live_lookup.py:247-26232).
Titik pemakaian saat scan
ScannerEngine membuat matcher hanya bila experimental.enable_cve_matching bernilai benar, lalu memanggil get_cve_matcher(config, auto_seed=True); bila DB tidak tersedia dan live lookup mati, factory mengembalikan None dan engine mencatat saran menjalankan python scripts/update_cve_database.py (core/scanner_engine.py:219-23933).
Factory get_cve_matcher menyusun live_config dari experimental maupun cve_intelligence dengan default max_results 3, keyword_search dan github_search benar, fetch_cve_detail benar, serta always_keyword_search salah, lalu seed DB dengan ensure_seed_if_empty(min_rows=5) sebelum memutuskan matcher layak dipakai (modules/cve_intelligence/cve_matcher.py:678-72734). Path DB diresolusi resolve_cve_db_path dengan prioritas experimental.cve_database_path, lalu cve_intelligence.database_path, lalu data/cve_intelligence.db (modules/cve_intelligence/cve_matcher.py:666-67535).
Setelah pemindaian satu URL mengumpulkan temuan, engine memanggil enrich_vulnerability untuk setiap item dan mencatat Enriched {len(vulnerabilities)} vulnerabilities with CVE data (core/scanner_engine.py:588-59536). Generator payload juga memakai matcher: match_technology_cves(tech_stack) lalu get_payloads_from_cves, dan payload CVE ditempatkan di depan dengan batas lima item per tipe serangan (core/ai_payload_generator.py:112-12237).
Pengayaan RAG berjalan terpisah setelah scan, di gerbang rag.enabled: engine memanggil rag.is_stale, membangun dan menyimpan index bila usang dan auto_rebuild aktif, atau memanggil rag.load; setelah itu setiap temuan yang belum punya cve_references diisi id hasil rag.search dan ditandai rag_matched (core/scanner_engine.py:878-91038). Query-nya adalah gabungan type, parameter, dan 200 karakter pertama evidence, dengan top_k dari rag.top_k (default 3) (core/scanner_engine.py:895-90739).
⚠️ Dua jalur itu tidak memakai resolver path yang sama. Matcher mengikuti
experimental.cve_database_path(modules/cve_intelligence/cve_matcher.py:666-67535), sedangkan langkah RAG membacacve_intelligence.database_pathdengan defaultdata/cve_intelligence.db(core/scanner_engine.py:884-88640). Bila path khusus hanya diisi padaexperimental,is_stalemembandingkan pickle dengan berkas default; ketika berkas default tidak ada,is_stalemengembalikanFalsedan index lama tetap dipakai walaupun basis data sebenarnya sudah diperbarui (modules/cve_intelligence/rag_index.py:68-7416).
Blok rag: sendiri tidak ada di berkas contoh; yang didokumentasikan hanya kunci experimental.* untuk CVE intelligence, termasuk cve_live_lookup, nvd_api_key, dan github_token (config/config.example.yaml:593-61141). Rincian gerbang dan urutan fase scan ada di Alur Scan.