Lompat ke konten
dokumentasi.id
Commit 51864a8
v1
Star1,9 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

10

Pelaporan, CVSS, dan Exporter

Paket reports/ masih berupa placeholder, jadi seluruh logika report berada di core/reporter_agent.py (reports/__init__.py:1-6). Exporter di core/exporters/ sengaja dibuat tipis: hanya menerjemahkan schema, tidak memanggil LLM, tidak memvalidasi ulang temuan, dan tidak mengubah PentestMemory (core/exporters/init.py:1-12).

Alur dari PentestMemory ke tiap sink

graph TD
  MEM["PentestMemory.findings"] --> EXEC["ReporterAgent.execute"]
  EXEC --> ES["generate_executive_summary"]
  EXEC --> TF["generate_technical_findings"]
  EXEC --> RM["generate_remediation_plan"]
  EXEC --> AT["generate_ai_trace"]
  ES --> FMT{"format"}
  TF --> FMT
  RM --> FMT
  AT --> FMT
  FMT -->|"markdown"| MD["_assemble_markdown_report"]
  FMT -->|"html"| HTML["_assemble_html_report"]
  FMT -->|"json"| JS["_assemble_json_report"]
  MD --> CLI["cli/commands/report.py"]
  HTML --> CLI
  JS --> CLI
  CLI --> SARIF["sarif.export"]
  CLI --> DD["defectdojo.export"]
  CLI --> SLACK["slack.build_payload + post"]
  style MEM fill:#336791,color:#fff
  style FMT fill:#f59e0b,color:#000
  style MD fill:#2d7d46,color:#fff
  style HTML fill:#2d7d46,color:#fff
  style JS fill:#2d7d46,color:#fff
  style SARIF fill:#8b5cf6,color:#fff
  style DD fill:#8b5cf6,color:#fff
  style SLACK fill:#8b5cf6,color:#fff
%% fig: Alur PentestMemory menjadi report lalu ke tiap sink
%% desc: ReporterAgent merakit empat bagian menjadi format markdown, html, atau json, lalu CLI menulis berkas dan memanggil exporter sarif, defectdojo, dan slack.
Fig. 1: Alur PentestMemory menjadi report lalu ke tiap sinkReporterAgent merakit empat bagian menjadi format markdown, html, atau json, lalu CLI menulis berkas dan memanggil exporter sarif, defectdojo, dan slack.

ReporterAgent.execute menerima satu argumen format dan mengembalikan dict berisi content, format, session_id, target, serta timestamp (core/reporter_agent.py:27-76). CLI menulis report["content"] ke berkas lalu memilih ekstensi dari nama format (cli/commands/report.py:85-96). Setelah report utama selesai, setiap nilai --export dijalankan pada blok yang sama (cli/commands/report.py:98-120).

Empat bagian report

BagianFungsiSumber data
Executive summarygenerate_executive_summaryget_findings_summary, get_findings_by_severity
Technical findingsgenerate_technical_findings_format_findings_detailed
Remediation plangenerate_remediation_plan_format_findings_detailed dan daftar f.target unik
AI decision tracegenerate_ai_tracememory.ai_decisions, thinking_chain, current_phase

generate_technical_findings dan generate_remediation_plan sama-sama memakai hasil _format_findings_detailed, sementara remediation menambahkan daftar affected system dari setiap f.target (core/reporter_agent.py:119-146).

generate_executive_summary tidak meminta LLM menebak rating risiko; nilainya diturunkan dari hitungan temuan (core/reporter_agent.py:78-117):

Kondisirisk_rating
summary["critical"] > 0CRITICAL
summary["high"] > 3HIGH
summary["high"] > 0 atau summary["medium"] > 5MEDIUM
selain ituLOW

_format_findings_detailed mengirim deskripsi dan evidence utuh sampai 4000 karakter per field, bukan potongan 200 karakter seperti versi sebelumnya (core/reporter_agent.py:337-370).

⚠️ REPORTER_TOKEN_COST_SECTION_PROMPT diimpor di modul reporter tetapi tidak pernah dipakai, sehingga appendix "AI Usage & Cost Summary" tidak pernah muncul di report mana pun (core/reporter_agent.py:11-18).

Tiga format perakitan

formatFungsi perakitEkstensi berkas di CLI
markdown_assemble_markdown_reportmd
html_assemble_html_reporthtml
json_assemble_json_reportjson
lainnyaraise ValueError(f"Unknown format: {format}")—

Report Markdown menyusun urutan tetap: Target Information, Executive Summary, tabel Findings Summary, Technical Findings, Remediation Plan, AI Decision Trace, dan Tools Executed dari _format_tool_executions (core/reporter_agent.py:171-223). Report JSON menyimpan metadata, executive_summary, findings_summary, findings hasil asdict, technical_findings, remediation_plan, ai_trace, dan tool_executions, lalu diserialisasi dengan json.dumps(report, indent=2, default=str) (core/reporter_agent.py:297-324). Report HTML merender bagian naratif lewat _markdown_to_html, yang memakai pustaka markdown dengan extension extra, tables, fenced_code, dan sane_lists; bila pustaka tidak terpasang, teks di-escape ke dalam <pre> (core/reporter_agent.py:383-404).

⚠️ Baris Total pada report Markdown dan HTML dihitung dengan len(self.memory.findings), sedangkan baris per severity memakai get_findings_summary() yang melewati false_positive (core/reporter_agent.py:171-223, core/memory.py:227-242). Total bisa lebih besar daripada jumlah baris di atasnya ketika ada temuan yang sudah ditandai false positive.

Verifikasi ulang skor vektor CVSS

core/cvss_calculator.py ada karena LLM reporter menulis vektor CVSS untuk setiap finding, dan tanpa validasi angka itu masuk ke report klien apa adanya (core/cvss_calculator.py:1-18). Hasil validasi dibungkus CvssResult yang membawa vector, base_score, severity, valid, dan error (core/cvss_calculator.py:42-50).

graph LR
  V["Finding.cvss_vector"] --> P["parse_and_score"]
  P -->|"prefix salah / metrik kurang"| ERR["CvssResult valid=False + error"]
  P --> B["_base_score"]
  B --> R["_roundup"]
  R --> S["_severity_band"]
  S --> C{"abs(base_score - cvss_score) <= 0.11"}
  C -->|"True"| OK["tanpa cvss_note"]
  C -->|"False"| WARN["CVSS vector does not match claimed score"]
  style V fill:#336791,color:#fff
  style ERR fill:#dc2626,color:#fff
  style WARN fill:#dc2626,color:#fff
  style OK fill:#2d7d46,color:#fff
  style C fill:#f59e0b,color:#000
  style S fill:#8b5cf6,color:#fff
%% fig: Verifikasi vektor CVSS sebelum report ditulis
%% desc: Vektor dari LLM di-parse, skor dasar dihitung ulang, lalu dibandingkan dengan skor klaim memakai toleransi 0.11.
Fig. 2: Verifikasi vektor CVSS sebelum report ditulisVektor dari LLM di-parse, skor dasar dihitung ulang, lalu dibandingkan dengan skor klaim memakai toleransi 0.11.

Hanya delapan base metric yang wajib ada — AV, AC, PR, UI, S, C, I, A — dan prefix vektor harus cocok dengan ^CVSS:3\.[01]/, sehingga vektor 3.0 tetap diterima (core/cvss_calculator.py:27-39). parse_and_score tidak pernah melempar exception: kegagalan diubah menjadi CvssResult dengan valid=False dan error terisi (core/cvss_calculator.py:53-80). Pesan yang mungkin muncul dari _parse_metrics adalah malformed metric token: {p}, missing required metric: {key}, dan invalid Scope value: {...} (core/cvss_calculator.py:105-127). _base_score memakai persamaan v3.1 dengan tabel weight PR yang berbeda saat Scope berubah, lalu _roundup membulatkan ke atas per 0.1 (core/cvss_calculator.py:130-154, core/cvss_calculator.py:167-181).

Rentang base_scoreseverity
0.0NONE
< 4.0LOW
< 7.0MEDIUM
< 9.0HIGH
>= 9.0CRITICAL

Bandingkan lewat validate_against_claimed(vector, claimed_score, tolerance=0.11): skor klaim None selalu dianggap cocok, dan vektor tidak valid selalu mengembalikan matches=False (core/cvss_calculator.py:83-99). _format_findings_detailed memakai hasilnya untuk menyisipkan penanda [⚠️ CVSS vector does not match claimed score — recompute before publishing] ke blok finding yang dikirim ke LLM (core/reporter_agent.py:337-370). Perilaku hitung ini diuji dengan vektor referensi dari FIRST.org, misalnya AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H yang harus menghasilkan 9.8 dan severity CRITICAL (tests/test_cvss_calculator.py:11-19).

⚠️ Docstring modul menjanjikan skor yang tidak cocok akan ditimpa dan field cvss_score_unverified ditandai (core/cvss_calculator.py:1-18), tetapi Finding tidak memiliki field tersebut dan validate_against_claimed tidak mengubah objek finding (core/memory.py:29-38). Satu-satunya efeknya adalah catatan teks di prompt.

Exporter SARIF, DefectDojo, dan Slack

--exportFungsi yang dipanggilKeluaran
sarifsarif.export(memory)report_{session_id}.sarif
defectdojodefectdojo.export(memory)report_{session_id}.defectdojo.json
slackslack.build_payload lalu slack.postHTTP status dari webhook

Penulisan ketiga berkas dan pengiriman webhook berada di satu blok for pada CLI (cli/commands/report.py:98-120).

SARIF

export menghasilkan satu runs[0] berisi tool.driver.rules, invocations, results, dan properties sesi (session_id, target, phase); dokumennya berupa dict biasa yang diserialisasi pemanggil (core/exporters/sarif.py:49-95). Temuan dengan false_positive dilewati dan setiap rule baru didaftarkan sekali per rule_id (core/exporters/sarif.py:60-66). _rule_id_for memilih CWE lebih dulu, lalu CVE, lalu {tool}/{slug-judul} (core/exporters/sarif.py:101-112).

Finding.severityresult.level
critical, higherror
mediumwarning
low, infonote

Pemetaan level beserta _VERSION = "2.1.0" dan _SCHEMA_URI ada di satu tempat (core/exporters/sarif.py:35-46). Setiap result membawa ruleId, level, message.text, locations[].physicalLocation.artifactLocation.uri, properties, dan fingerprints; security-severity ditulis sebagai string satu desimal f"{f.cvss_score:.1f}" agar dibaca GitHub code-scanning (core/exporters/sarif.py:151-187).

⚠️ Docstring di modul SARIF menyebut fingerprint guardian/execution, sedangkan kode menulis key guardian/execution/v1 (core/exporters/sarif.py:151-187). Konsumen yang mengunci nama key dari docstring tidak akan menemukan fingerprint-nya.

DefectDojo

export mengembalikan {"findings": [...]} dengan temuan non-FP saja, dan _SEVERITY_MAP mengubah severity menjadi bentuk berhuruf besar seperti Critical atau Info (core/exporters/defectdojo.py:22-35). Field opsional hanya diisi bila datanya ada: cve, cvssv3_score, cvssv3, cwe sebagai integer hasil int(f.cwe[4:]), dan url dari f.target (core/exporters/defectdojo.py:38-61). Deskripsi menggabungkan description, bagian ## Evidence yang dipotong 2000 karakter, dan ## Execution ID (core/exporters/defectdojo.py:64-72).

Slack dan Discord

build_payload menyusun satu field text berisi judul sesi, target, satu baris hitungan per severity dengan emoji, lalu maksimal top_n=5 temuan teratas hasil sorting _severity_rank (core/exporters/slack.py:30-66). Emoji dipilih dari _SEVERITY_EMOJI, misalnya :rotating_light: untuk critical dan :information_source: untuk info (core/exporters/slack.py:21-27). post mengirim JSON lewat urllib.request dengan timeout=10 dan mengembalikan resp.status (core/exporters/slack.py:74-84).

Menjalankan guardian report

OpsiDefaultKeterangan
--session, -swajibSession ID yang dilaporkan
--format, -fmarkdownmarkdown, html, atau json
--output, -o./reports/report_{session_id}.{ext}Path berkas report
--config, -cconfig/guardian.yamlBerkas konfigurasi
--export, -ekosongDapat diulang; sarif, defectdojo, slack
--slack-webhookenv GUARDIAN_SLACK_WEBHOOKWajib saat --export slack

Deklarasi opsi beserta _VALID_EXPORTS = {"sarif", "defectdojo", "slack"} ada di satu tempat (cli/commands/report.py:13-38). Nilai --export divalidasi di awal agar proses gagal cepat: format asing mencetak Unknown export format '...' lalu typer.Exit(1) (cli/commands/report.py:59-66). Bila --output tidak diberikan, ekstensi dipilih dari nama format, dan direktori induk dibuat dengan mkdir(parents=True, exist_ok=True) (cli/commands/report.py:85-96). Session dibaca dari ./reports/session_{session_id}.json hasil SessionPersister workflow; bila berkas tidak ada, perintah berhenti dengan pesan Session not found (cli/commands/report.py:68-72).

⚠️ --export slack tanpa --slack-webhook menghentikan perintah dengan typer.Exit(1), padahal berkas report utama sudah ditulis lebih dulu di blok try yang sama (cli/commands/report.py:98-120).

Perilaku exporter diuji langsung pada PentestMemory buatan: fixture menambahkan satu temuan false_positive untuk memastikan temuan itu tidak ikut terekspor (tests/test_exporters.py:35-47), bentuk dokumen dan pemetaan level diperiksa menjadi ["error", "warning"] (tests/test_exporters.py:50-66), dan setiap result wajib punya guardian/execution/v1 serta rule CWE tanpa CVE wajib menunjuk cwe.mitre.org (tests/test_exporters.py:78-91).