Lompat ke konten
dokumentasi.id
Commit e4397d1
v1
Star2,4 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

2.9

Scan Diff dan Retest

Deep Eye memakai dua mekanisme identitas temuan yang berbeda: kunci identity (type, normalized_url, parameter) untuk diff dan --retest-new, serta fingerprint SHA-256 untuk dedupe. Ketiganya bertumpu pada normalisasi URL yang tidak sama, sehingga satu temuan bisa dianggap duplikat oleh dedupe tetapi tetap dihitung berbeda oleh diff. Untuk urutan fase scan yang lengkap, lihat Alur Scan; untuk bentuk laporan akhir, lihat Pelaporan dan Ekspor.

Dua kunci identitas

KunciFungsi pembentukKomponenNormalisasi URLPemakai
identity_identity / _identity_no_sevtype, url, parameter, dan severity hanya pada _identitynormalize_url — scheme dan host dihuruf-kecilkan, garis miring akhir path dibuang, query param diurutkan, fragment dibuangdiff_scans, --retest-new
fingerprintfingerprinttype, path hasil normalize_path, parameter, root_cause_hintnormalize_path — hanya scheme://netloc/path, query dan fragment tidak ikutdedupe_findings

Pasangan _identity dan _identity_no_sev di core/scan_diff.py menjadi sumber kedua kunci diff; _identity menambahkan severity ke dalam tuple (core/scan_diff.py:31-47). normalize_url menurunkan scheme dan host ke huruf kecil, membuang garis miring di akhir path, mengurutkan hasil parse_qsl sebelum urlencode, lalu membuang fragment (core/scan_diff.py:10-28).

Sisi fingerprint memakai normalize_path yang hanya menyusun scheme://netloc/path tanpa query dan fragment (utils/finding_fingerprint.py:10-18). root_cause_hint merangkai type huruf kecil, payload maksimal 80 karakter, dan evidence maksimal 120 karakter, lalu merapatkan seluruh whitespace menjadi satu spasi (utils/finding_fingerprint.py:21-25). fingerprint menggabungkan type|normalize_path(url)|parameter|root_cause_hint menjadi satu string, menghitung SHA-256, dan mengambil 16 karakter hex pertama (utils/finding_fingerprint.py:28-38). identity_tuple menyediakan bentuk tiga elemen tanpa severity untuk kebutuhan lain (utils/finding_fingerprint.py:67-72).

graph LR
  F["finding dict"] --> NU["normalize_url"]
  F --> NP["normalize_path"]
  F --> RCH["root_cause_hint"]
  RCH --> FP["fingerprint sha256 16 hex"]
  NP --> FP
  NU --> ID["_identity_no_sev"]
  NU --> IDS["_identity plus severity"]
  ID --> RT["--retest-new filter"]
  ID --> DS["diff_scans"]
  IDS --> DS
  FP --> DD["dedupe_findings"]
  style FP fill:#336791,color:#fff
  style DD fill:#2d7d46,color:#fff
  style DS fill:#8b5cf6,color:#fff
  style RT fill:#8b5cf6,color:#fff
  style ID fill:#f59e0b,color:#000
  style IDS fill:#f59e0b,color:#000
  style NU fill:#f59e0b,color:#000
  style NP fill:#f59e0b,color:#000
  %% fig: Dua jalur identitas temuan
  %% desc: normalize_url membentuk kunci identity untuk diff dan retest, sedangkan normalize_path menyusun bahan fingerprint untuk dedupe.
Fig. 1: Dua jalur identitas temuannormalize_url membentuk kunci identity untuk diff dan retest, sedangkan normalize_path menyusun bahan fingerprint untuk dedupe.

Dedupe dengan fingerprint

dedupe_findings mengelompokkan temuan per fingerprint, menuliskan fingerprint ke setiap dict hasil, dan mempertahankan urutan kemunculan pertama; dengan default prefer_higher_severity=True, peringkat critical:4 sampai info:0 menentukan temuan mana yang disimpan (utils/finding_fingerprint.py:41-64). Karena fingerprint tidak memuat severity, dua temuan yang hanya berbeda severity tetap dianggap satu, dan yang tersisa adalah severity tertinggi (tests/test_features_1_19.py:29-33).

Pemanggilan terjadi di akhir scan: bila reporting.dedupe bernilai True, self.vulnerabilities diganti hasil dedupe_findings lalu severity_summary dihitung ulang (core/scanner_engine.py:851-858). Berkas contoh menetapkan nilai itu true (config/config.example.yaml:391).

Siklus status temuan

diff_scans lebih dulu membuang temuan yang tidak punya type atau url, lalu membangun indeks _identity dan indeks _identity_no_sev untuk kedua berkas (core/scan_diff.py:56-71). Temuan dengan identity tanpa severity yang sama tetapi severity berbeda dicatat sebagai pasangan baseline/current di severity_changed, dan kuncinya dikumpulkan di sev_changed_no_sev_keys agar tidak ikut masuk ke new atau fixed (core/scan_diff.py:73-93). Daftar unchanged adalah irisan kedua indeks (core/scan_diff.py:95).

stateDiagram-v2
  [*] --> ScanJSON: temuan ditulis ke vulnerabilities
  ScanJSON --> New: identity tidak ada di baseline
  ScanJSON --> Unchanged: identity dan severity sama
  ScanJSON --> SeverityChanged: identity sama, severity berbeda
  New --> Unchanged: terlihat lagi pada scan berikutnya
  SeverityChanged --> Unchanged: severity kembali sama
  Unchanged --> Fixed: identity hilang pada scan berikutnya
  New --> Fixed: hilang sebelum sempat unchanged
  Fixed --> [*]
  style ScanJSON fill:#336791,color:#fff
  style New fill:#dc2626,color:#fff
  style Fixed fill:#2d7d46,color:#fff
  style Unchanged fill:#8b5cf6,color:#fff
  style SeverityChanged fill:#f59e0b,color:#000
  %% fig: Perpindahan status temuan antar scan
  %% desc: diff_scans menetapkan new, fixed, unchanged, atau severity_changed berdasarkan identity dan severity kedua berkas.
Fig. 2: Perpindahan status temuan antar scandiff_scans menetapkan new, fixed, unchanged, atau severity_changed berdasarkan identity dan severity kedua berkas.
Field summaryArti
newidentity ada di current dan tidak ada di baseline
fixedidentity ada di baseline dan tidak ada di current
unchangedidentity ada di kedua berkas
severity_changedidentity tanpa severity sama, severity berbeda
net_deltanew dikurangi fixed; nilai negatif berarti perbaikan

Kelima nilai itu dihitung dari panjang daftar masing-masing, dengan net_delta sebagai len(new_list) - len(fixed_list) (core/scan_diff.py:97-103). Hasil diff_scans mengembalikan kartu baseline dan current berisi target, scan_time yang jatuh ke start_time bila end_time kosong, serta vuln_count, di samping summary dan empat daftar temuan (core/scan_diff.py:105-121).

⚠️ Indeks dibangun lewat dict comprehension {_identity(v): v for v in ...}, jadi dua temuan dalam satu berkas scan dengan identity dan severity identik saling menimpa dan hanya kemunculan terakhir yang bertahan (core/scan_diff.py:64-65). Hitungan diff bisa lebih kecil daripada jumlah baris di berkas scan.

Mode --diff

FlagNilaiKeterangan
--diffBASELINE CURRENTnargs=2, menjalankan diff tanpa scan
--diff-outputpathdefault reports/diff_<timestamp>.<ext>
--diff-formathtml, json, csvdefault html

Ketiga flag itu dideklarasikan di parse_arguments (deep_eye.py:108-128), dan main memeriksa args.diff lebih dulu sehingga scan dilewati sepenuhnya (deep_eye.py:248-250). _run_diff_mode memuat kedua berkas via load_scan_json; FileNotFoundError atau json.JSONDecodeError menghasilkan pesan Failed to load scan JSON: {e} dan exit code 2. Bila --diff-output kosong, direktori reports dibuat dan nama berkas diturunkan dari timestamp serta format. Fungsi ini mencetak ringkasan New, Fixed, Severity Changed, Unchanged, dan Net Delta, lalu menutup dengan exit code 0 (deep_eye.py:193-235). Alur ini juga diringkas di berkas rujukan (docs/SCAN_FLOW.md:71-81).

Renderer keluaran

FungsiBerkasCiri keluaran
render_jsonutils/exports/diff_renderer.pyjson.dump dengan indent=2 dan ensure_ascii=False
render_csvutils/exports/diff_renderer.py9 kolom, diawali BOM \ufeff, satu baris per temuan berstatus new, fixed, unchanged, atau severity_changed
render_htmlutils/exports/diff_renderer.pyhalaman mandiri dengan kartu meta, blok statistik, dan <details open> per daftar temuan

render_csv menulis kolom status, type, severity, url, parameter, baseline_severity, current_severity, payload, evidence, dan setiap baris dirakit _row yang memotong evidence pada 500 karakter (utils/exports/diff_renderer.py:28-51). render_csv sendiri menulis BOM lalu header sebelum baris data (utils/exports/diff_renderer.py:7-38).

render_html menyusun kartu Baseline dan Current dari target, scan_time, dan vuln_count, empat blok statistik, lalu tabel terpisah untuk New Vulnerabilities dan Fixed Vulnerabilities serta tabel Severity Changed berisi panah baseline → current (utils/exports/diff_renderer.py:54-99). Temuan unchanged tidak dibuatkan tabel, hanya disebut sebagai kalimat penutup berisi jumlahnya (utils/exports/diff_renderer.py:101-138). Semua nilai melewati _esc yang mengganti &, <, >, dan ", sehingga type berisi <script> tampil sebagai teks (utils/exports/diff_renderer.py:141-149).

--retest-new

Flag --retest-new BASELINE_JSON menyaring hasil scan yang baru selesai (deep_eye.py:130-136). Setelah scanner.scan selesai, _identity_no_sev dipakai untuk membangun base_keys dari vulnerabilities baseline yang punya type dan url, lalu results['vulnerabilities'] disaring sehingga hanya temuan yang kuncinya tidak ada di baseline yang bertahan. Proses ini mencetak Retest-new: kept N/M new findings vs <path>; kegagalan apa pun ditangkap dan dilaporkan sebagai Retest-new skipped: {e} tanpa menghentikan pelaporan (deep_eye.py:349-369).

⚠️ --retest-new memakai _identity_no_sev, jadi temuan yang hanya berubah severity ikut dibuang bersama temuan lama. Filter ini cocok untuk pertanyaan CI "apa yang baru sejak run terakhir", tetapi bukan pengganti --diff yang tetap melaporkan severity_changed sebagai kategori tersendiri.

Ringkasan perbedaan kedua kunci juga dicatat di dokumentasi arsitektur (docs/ARCHITECTURE.md:88-94).