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

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-39, sedangkan parse_output mengubah stdout menjadi dict terstruktur tools/base_tool.py:41-44. execute() diwarisi dan tidak perlu dioverride docs/TOOLS_DEVELOPMENT_GUIDE.md:15-31.

AnggotaSifatPerilaku
get_commandabstrakMembangun argumen; target tidak boleh diubah tools/base_tool.py:36-39
parse_outputabstrakMengubah stdout menjadi dict terstruktur tools/base_tool.py:41-44
executewarisanCek ketersediaan, spawn proses, streaming, timeout, lalu parsing tools/base_tool.py:46-63
_check_installationprivatshutil.which terhadap self.tool_name tools/base_tool.py:209-211
get_versionpublikMenjalankan --version dengan timeout 5 detik tools/base_tool.py:213-224
_skipped_resultprivatHasil success=False dengan skipped=True tools/base_tool.py:179-207

execute() dideklarasikan tidak pernah melempar exception; kegagalan selalu dinormalkan menjadi dict tools/base_tool.py:46-63. Bila binary tidak terpasang, ia langsung mengembalikan hasil skip tanpa menyentuh subprocess tools/base_tool.py:65-69. Jalur error dan skip memakai _error_result serta _skipped_result agar pemanggil bisa melewati langkah dengan aman tools/base_tool.py:179-207.

Batas waktu dibaca dari pentest.tool_timeout dengan default 300 detik tools/base_tool.py:80. Proses dijalankan lewat asyncio.create_subprocess_exec dengan stdout dan stderr berupa pipe tools/base_tool.py:84-88. 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 hasilIsi
successTrue setelah proses selesai dan parsing berhasil
toolself.tool_name
commandargumen yang digabung dengan spasi
exit_codeprocess.returncode
durationselisih detik dari start_time
raw_outputstdout setelah strip_control_chars
parsedkeluaran parse_output

Semua kunci itu dibentuk di satu tempat yang sama tools/base_tool.py:141-157.

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.
Fig. 1: Satu eksekusi tool dari pemilihan sampai parsingToolAgent 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-79. 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-18. Paket tools sendiri hanya mengekspor BaseTool, sehingga impor paket tidak memuat kelas konkret tools/__init__.py:3-6.

_import_tool memecah spec pada : lalu memakai importlib untuk mengambil kelasnya core/tool_agent.py:157-162. _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-205. _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.
Fig. 2: Resolusi lazy dari nama tool ke instance wrapperRegistry 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-91. Contoh lengkap satu wrapper minimal ada di README yang sama tools/README.md:75-91, dan status pemasangan bisa diperiksa lewat get_available_tools() tools/README.md:131-138. Panduan developer menambahkan langkah registrasi di core/tool_agent.py serta pembaruan prompt selector docs/TOOLS_DEVELOPMENT_GUIDE.md:267-296.

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-63 dan get_command httpx tools/httpx.py:18-57. Keduanya membaca keluaran JSON line demi line di parse_output tools/nuclei.py:65-108 dan tools/httpx.py:59-91.

⚠️ PlaywrightScreenshotTool menyimpang dari kontrak. Wrapper ini tidak melakukan shell out; get_command hanya stub dan parse_output no-op, sementara seluruh pekerjaan dilakukan oleh execute yang dioverride tools/playwright_screenshot.py:65-72. Alasan dan konsekuensinya dijelaskan di docstring modul tools/playwright_screenshot.py:10-22, dan eksekusi sebenarnya ada di override execute tools/playwright_screenshot.py:74-131. Ketersediaannya diperiksa lewat impor playwright, bukan shutil.which, karena playwright adalah pustaka Python tools/playwright_screenshot.py:51-60.

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-423. Sebelum menyentuh LLM, ia mencoba ranker offline; bila ranker yakin, hasilnya langsung dikembalikan beserta alasannya core/tool_agent.py:254-263. Ranker hanya dicoba bila ai.use_learned_ranker aktif core/tool_agent.py:470-471. Seluruh jalur itu, termasuk penolakan atas nama tool yang tidak lagi terdaftar, ada di _predict_with_ranker core/tool_agent.py:463-504.

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-297. _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-448.

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.
Fig. 3: Dua jalur pemilihan toolRanker 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-392. Workflow memanggilnya dengan stream_callback yang mencetak tiap baris ke console, dan target selalu berupa target CLI yang persis sama core/workflow.py:247-260. Untuk satu tool yang sudah dipilih, configure_tool menyusun parameter optimal beserta justifikasinya core/tool_agent.py:311-342.

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-43. Target tidak pernah disimpan mentah; _bucket_target hanya mengembalikan url, ip, domain, atau unknown core/telemetry.py:46-56. Aturan anonimisasi menyatakan bahwa output mentah, command, dan secret dibuang seluruhnya, dan fitur ini mati secara default core/telemetry.py:1-19.

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-110. write_jsonl menambahkan baris ke berkas JSONL core/telemetry.py:113-122, dan export_session_file serta export_directory membungkusnya untuk satu sesi atau seluruh direktori reports/ core/telemetry.py:125-142.

SubcommandArgumenPerilaku
exportsource, --outMenulis JSONL anonim dari satu sesi atau direktori cli/commands/telemetry.py:30-42
trainjsonl, --out, --min-confidenceMelatih ranker dan menyimpan pickle cli/commands/telemetry.py:45-59
status—Memuat ~/.guardian/ranker.pkl dan menampilkan top-3 per pasangan phase dan target cli/commands/telemetry.py:62-92

Ketiga subcommand terdaftar pada satu telemetry_app cli/commands/telemetry.py:26. 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-13.

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-50. Model default disimpan sebagai pickle di ~/.guardian/ranker.pkl dan tidak pernah diperbarui otomatis core/learners/tool_ranker.py:1-24.

_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-88. Skor akhir menggabungkan empat suara dengan bobot tetap, lalu dinormalisasi lewat _softmax core/learners/tool_ranker.py:90-111 dan core/learners/tool_ranker.py:114-121.

FiturBobotCatatan
by_phase_target3.0Pasangan phase dan target, sinyal terkuat core/learners/tool_ranker.py:90-111
by_target1.5Cadangan saat sel joint kosong core/learners/tool_ranker.py:90-111
by_phase1.5Cadangan saat sel joint kosong core/learners/tool_ranker.py:90-111
tool_totals0.1Prior sebelum-sesudah, mencegah skor nol core/learners/tool_ranker.py:90-111

Ambang keyakinan default adalah 0.7 core/learners/tool_ranker.py:134-137. 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-183. save dan load menulis serta membaca dict berisi tabel hitungan dan min_confidence core/learners/tool_ranker.py:187-202.

⚠️ Ranker tidak pernah menjadi satu-satunya penentu. Bila model belum dilatih, gagal dimuat, atau memilih nama yang sudah tidak ada di registry, ToolAgent kembali ke selector LLM core/tool_agent.py:463-504.

Risiko tool dan gerbang konfirmasi

Setiap tool punya kelas risiko di TOOL_RISK_CLASS: passive, active, intrusive, atau destructive core/tool_agent.py:88-154. Workflow membacanya di _confirm_tool; tool passive selalu lanjut, dan tool destructive diblokir selama safe_mode aktif meskipun --yes diberikan core/workflow.py:444-465. Detail kebijakan scope, safe_mode, dan sanitasi ada di Keamanan, Scope, dan Sanitasi.