8
Integrasi Tool dan Pemilihan Tool
Guardian membungkus setiap tool pentest eksternal dalam satu kelas turunan BaseTool, lalu memilih wrapper mana yang dipakai untuk sebuah objective. Pemilihan berjalan dua tahap: ranker offline sebagai pre-filter cepat, dan selector LLM sebagai fallback. Halaman ini menjelaskan kontrak wrapper, registry lazy, jalur pemilihan, serta telemetri anonim yang melatih ranker. Penjadwalan langkah tool di dalam workflow dibahas di Engine Workflow DSL v2, sedangkan gerbang scope dan konfirmasi dibahas di Keamanan, Scope, dan Sanitasi.
Kontrak BaseTool
BaseTool adalah kelas abstrak dengan dua metode wajib. get_command mengembalikan List[str] dan wajib memakai target persis seperti yang diberikan tools/base_tool.py:36-391, sedangkan parse_output mengubah stdout menjadi dict terstruktur tools/base_tool.py:41-442. execute() diwarisi dan tidak perlu dioverride docs/TOOLS_DEVELOPMENT_GUIDE.md:15-313.
| Anggota | Sifat | Perilaku |
|---|---|---|
get_command | abstrak | Membangun argumen; target tidak boleh diubah tools/base_tool.py:36-391 |
parse_output | abstrak | Mengubah stdout menjadi dict terstruktur tools/base_tool.py:41-442 |
execute | warisan | Cek ketersediaan, spawn proses, streaming, timeout, lalu parsing tools/base_tool.py:46-634 |
_check_installation | privat | shutil.which terhadap self.tool_name tools/base_tool.py:209-2115 |
get_version | publik | Menjalankan --version dengan timeout 5 detik tools/base_tool.py:213-2246 |
_skipped_result | privat | Hasil success=False dengan skipped=True tools/base_tool.py:179-2077 |
execute() dideklarasikan tidak pernah melempar exception; kegagalan selalu dinormalkan menjadi dict tools/base_tool.py:46-634. Bila binary tidak terpasang, ia langsung mengembalikan hasil skip tanpa menyentuh subprocess tools/base_tool.py:65-698. Jalur error dan skip memakai _error_result serta _skipped_result agar pemanggil bisa melewati langkah dengan aman tools/base_tool.py:179-2077.
Batas waktu dibaca dari pentest.tool_timeout dengan default 300 detik tools/base_tool.py:809. Proses dijalankan lewat asyncio.create_subprocess_exec dengan stdout dan stderr berupa pipe tools/base_tool.py:84-8810. Bila stream_callback diberikan, kedua stream dibaca baris per baris di dalam asyncio.gather yang dibungkus asyncio.wait_for, dan proses di-kill() saat timeout tools/base_tool.py:94-119. Tanpa callback, mode buffered memakai process.communicate() dengan batas waktu yang sama tools/base_tool.py:120-132. Sebelum disimpan atau dikirim ke LLM, kedua stream melewati strip_control_chars agar escape code dari output tool tidak bocor tools/base_tool.py:135-139. Setelah itu parse_output dipanggil dan dict hasil dibentuk tools/base_tool.py:141-157.
| Kunci hasil | Isi |
|---|---|
success | True setelah proses selesai dan parsing berhasil |
tool | self.tool_name |
command | argumen yang digabung dengan spasi |
exit_code | process.returncode |
duration | selisih detik dari start_time |
raw_output | stdout setelah strip_control_chars |
parsed | keluaran parse_output |
Semua kunci itu dibentuk di satu tempat yang sama tools/base_tool.py:141-15714.
sequenceDiagram
participant WF as Workflow._execute_step
participant TA as ToolAgent
participant BT as BaseTool
participant P as subprocess
WF->>TA: execute_tool
TA->>TA: _get_tool memakai _import_tool
alt nama tidak terdaftar
TA-->>WF: success=False
else tool tidak terpasang
TA-->>WF: skipped=True
else tersedia
TA->>BT: await execute
BT->>P: create_subprocess_exec
loop tiap baris output
BT-->>WF: stream_callback per baris
end
BT->>BT: strip_control_chars lalu parse_output
BT-->>TA: dict hasil parse
TA->>TA: add_tool_execution
TA-->>WF: parsed dan duration
end
%% fig: Satu eksekusi tool dari pemilihan sampai parsing
%% desc: ToolAgent menyelesaikan nama tool ke instance, BaseTool menjalankan subprocess sambil men-stream baris, lalu hasilnya di-parse dan dicatat ke memory.Registry lazy dan plugin entry-point
TOOL_REGISTRY memetakan nama tool ke string module:Class untuk seluruh wrapper in-tree core/tool_agent.py:19-7915. Komentar di atas tabel menjelaskan alasannya: guardian --help tidak lagi membayar biaya impor semua wrapper beserta panggilan shutil.which-nya core/tool_agent.py:15-1816. Paket tools sendiri hanya mengekspor BaseTool, sehingga impor paket tidak memuat kelas konkret tools/__init__.py:3-617.
_import_tool memecah spec pada : lalu memakai importlib untuk mengambil kelasnya core/tool_agent.py:157-16218. _discover_plugin_tools menggabungkan registry in-tree dengan entry point guardian.tools, menyimpan hasilnya sekali per proses di _TOOL_REGISTRY_CACHE, dan tool in-tree menang saat ada nama bentrok core/tool_agent.py:169-20519. _get_tool menginstansiasi wrapper pada pemakaian pertama lalu menyimpannya di _instances core/tool_agent.py:219-230. Properti available_tools hanyalah shim yang mengembalikan cache tersebut core/tool_agent.py:232-238, sementara get_available_tools() memaksa instansiasi seluruh registry untuk melaporkan status pemasangan core/tool_agent.py:450-459.
graph LR
REG["TOOL_REGISTRY module:Class"] --> DISC["_discover_plugin_tools"]
EP["entry point guardian.tools"] --> DISC
DISC --> CACHE["_TOOL_REGISTRY_CACHE"]
DISC --> GET["ToolAgent._get_tool"]
GET --> IMP["_import_tool"]
IMP --> INST["instance wrapper"]
GET --> INSTS["_instances cache"]
style REG fill:#f59e0b,color:#000
style EP fill:#f59e0b,color:#000
style DISC fill:#2d7d46,color:#fff
style CACHE fill:#8b5cf6,color:#fff
style INSTS fill:#8b5cf6,color:#fff
%% fig: Resolusi lazy dari nama tool ke instance wrapper
%% desc: Registry in-tree digabung dengan entry point plugin sekali per proses, lalu _get_tool mengimpor dan menyimpan instance pada pemakaian pertama.Langkah menambah wrapper baru didokumentasikan di README direktori tool: warisi BaseTool, implementasikan get_command dan parse_output, lalu daftarkan namanya tools/README.md:75-9123. Contoh lengkap satu wrapper minimal ada di README yang sama tools/README.md:75-9123, dan status pemasangan bisa diperiksa lewat get_available_tools() tools/README.md:131-13824. Panduan developer menambahkan langkah registrasi di core/tool_agent.py serta pembaruan prompt selector docs/TOOLS_DEVELOPMENT_GUIDE.md:267-29625.
Dua wrapper yang mengikuti kontrak penuh adalah nuclei dan httpx. Keduanya memprioritaskan parameter workflow di atas config dan default: get_command nuclei tools/nuclei.py:18-6326 dan get_command httpx tools/httpx.py:18-5727. Keduanya membaca keluaran JSON line demi line di parse_output tools/nuclei.py:65-10828 dan tools/httpx.py:59-9129.
⚠️
PlaywrightScreenshotToolmenyimpang dari kontrak. Wrapper ini tidak melakukan shell out;get_commandhanya stub danparse_outputno-op, sementara seluruh pekerjaan dilakukan olehexecuteyang dioverride tools/playwright_screenshot.py:65-7230. Alasan dan konsekuensinya dijelaskan di docstring modul tools/playwright_screenshot.py:10-2231, dan eksekusi sebenarnya ada di overrideexecutetools/playwright_screenshot.py:74-13132. Ketersediaannya diperiksa lewat imporplaywright, bukanshutil.which, karena playwright adalah pustaka Python tools/playwright_screenshot.py:51-6033.
Jalur pemilihan: ranker offline lalu selector LLM
ToolAgent.execute lebih dulu mengklasifikasikan target menjadi url, ip, domain, atau unknown lewat _detect_target_type core/tool_agent.py:412-42334. Sebelum menyentuh LLM, ia mencoba ranker offline; bila ranker yakin, hasilnya langsung dikembalikan beserta alasannya core/tool_agent.py:254-26335. Ranker hanya dicoba bila ai.use_learned_ranker aktif core/tool_agent.py:470-47136. Seluruh jalur itu, termasuk penolakan atas nama tool yang tidak lagi terdaftar, ada di _predict_with_ranker core/tool_agent.py:463-50437.
Bila ranker abstain, execute menyusun TOOL_SELECTION_PROMPT dari konteks memory, daftar tool terpasang, dan constraint config seperti safe_mode serta rate_limit, lalu memanggil think core/tool_agent.py:265-29738. _parse_selection membaca blok TOOL:, ARGUMENTS:, dan EXPECTED_OUTPUT: dari jawaban model, dengan nmap sebagai default bila blok TOOL: tidak ada core/tool_agent.py:425-44839.
graph TD
A["ToolAgent.execute"] --> B["_detect_target_type"]
B --> C{"ai.use_learned_ranker"}
C -->|false| LLM["TOOL_SELECTION_PROMPT"]
C -->|true| D["ToolRanker.load"]
D --> E["predict_with_fallback"]
E -->|confidence cukup| PICK["tool dari ranker"]
E -->|None| LLM
LLM --> F["_parse_selection"]
F --> PICK2["tool dan arguments"]
PICK --> EX["execute_tool"]
PICK2 --> EX
style C fill:#f59e0b,color:#000
style LLM fill:#f59e0b,color:#000
style D fill:#336791,color:#fff
style PICK fill:#2d7d46,color:#fff
style PICK2 fill:#8b5cf6,color:#fff
style EX fill:#2d7d46,color:#fff
%% fig: Dua jalur pemilihan tool
%% desc: Ranker offline dipakai lebih dulu saat diaktifkan dan yakin; bila abstain, selector LLM memilih tool dan menuliskan argumennya.execute_tool menolak nama yang tidak ada di registry, melewati tool yang tidak terpasang, dan mencatat eksekusi sukses ke memory sebagai ToolExecution core/tool_agent.py:363-39240. Workflow memanggilnya dengan stream_callback yang mencetak tiap baris ke console, dan target selalu berupa target CLI yang persis sama core/workflow.py:247-26041. Untuk satu tool yang sudah dipilih, configure_tool menyusun parameter optimal beserta justifikasinya core/tool_agent.py:311-34242.
Telemetri anonim
Telemetri menyimpan satu baris per eksekusi tool berisi target_type, phase, tool, duration, findings_yielded, success, prior_tool_count, dan prior_findings_count core/telemetry.py:31-4343. Target tidak pernah disimpan mentah; _bucket_target hanya mengembalikan url, ip, domain, atau unknown core/telemetry.py:46-5644. Aturan anonimisasi menyatakan bahwa output mentah, command, dan secret dibuang seluruhnya, dan fitur ini mati secara default core/telemetry.py:1-1945.
session_to_rows membaca session_<id>.json dan tetap mengeluarkan baris untuk eksekusi yang gagal atau di-skip, sehingga learner bisa memodelkan tool yang cenderung terlewat pada tipe target tertentu core/telemetry.py:59-11046. write_jsonl menambahkan baris ke berkas JSONL core/telemetry.py:113-12247, dan export_session_file serta export_directory membungkusnya untuk satu sesi atau seluruh direktori reports/ core/telemetry.py:125-14248.
| Subcommand | Argumen | Perilaku |
|---|---|---|
export | source, --out | Menulis JSONL anonim dari satu sesi atau direktori cli/commands/telemetry.py:30-4249 |
train | jsonl, --out, --min-confidence | Melatih ranker dan menyimpan pickle cli/commands/telemetry.py:45-5950 |
status | — | Memuat ~/.guardian/ranker.pkl dan menampilkan top-3 per pasangan phase dan target cli/commands/telemetry.py:62-9251 |
Ketiga subcommand terdaftar pada satu telemetry_app cli/commands/telemetry.py:2652. Docstring modul menegaskan bahwa tidak ada data yang keluar dari mesin lokal; sesi hanya ditransformasi dari JSON menjadi JSONL anonim cli/commands/telemetry.py:1-1353.
Ranker offline
ToolRanker dilatih dari baris telemetri dan memprediksi tool terbaik dari empat fitur: target_type, phase, prior_tool_count, dan prior_findings_count core/learners/tool_ranker.py:37-5054. Model default disimpan sebagai pickle di ~/.guardian/ranker.pkl dan tidak pernah diperbarui otomatis core/learners/tool_ranker.py:1-2455.
_CountTable.fit hanya menghitung baris yang sukses atau menghasilkan finding, dan memberi bobot sebesar jumlah finding agar tool yang produktif lebih dominan core/learners/tool_ranker.py:70-8856. Skor akhir menggabungkan empat suara dengan bobot tetap, lalu dinormalisasi lewat _softmax core/learners/tool_ranker.py:90-11157 dan core/learners/tool_ranker.py:114-12158.
| Fitur | Bobot | Catatan |
|---|---|---|
by_phase_target | 3.0 | Pasangan phase dan target, sinyal terkuat core/learners/tool_ranker.py:90-11157 |
by_target | 1.5 | Cadangan saat sel joint kosong core/learners/tool_ranker.py:90-11157 |
by_phase | 1.5 | Cadangan saat sel joint kosong core/learners/tool_ranker.py:90-11157 |
tool_totals | 0.1 | Prior sebelum-sesudah, mencegah skor nol core/learners/tool_ranker.py:90-11157 |
Ambang keyakinan default adalah 0.7 core/learners/tool_ranker.py:134-13759. predict mengembalikan top-k (tool, probability) terurut menurun, sedangkan predict_with_fallback mengembalikan None bila probabilitas top-1 di bawah ambang itu core/learners/tool_ranker.py:171-18360. save dan load menulis serta membaca dict berisi tabel hitungan dan min_confidence core/learners/tool_ranker.py:187-20261.
⚠️ Ranker tidak pernah menjadi satu-satunya penentu. Bila model belum dilatih, gagal dimuat, atau memilih nama yang sudah tidak ada di registry,
ToolAgentkembali ke selector LLM core/tool_agent.py:463-50437.
Risiko tool dan gerbang konfirmasi
Setiap tool punya kelas risiko di TOOL_RISK_CLASS: passive, active, intrusive, atau destructive core/tool_agent.py:88-15462. Workflow membacanya di _confirm_tool; tool passive selalu lanjut, dan tool destructive diblokir selama safe_mode aktif meskipun --yes diberikan core/workflow.py:444-46563. Detail kebijakan scope, safe_mode, dan sanitasi ada di Keamanan, Scope, dan Sanitasi.