2
Arsitektur Sistem
Guardian tersusun sebagai empat lapisan yang saling bergantung: lapisan CLI, lapisan engine workflow, lapisan agen, dan lapisan tool. Setiap lapisan hanya berbicara ke lapisan di bawahnya lewat satu antarmuka sempit, sehingga penggantian provider AI atau penambahan wrapper tool tidak menyentuh kode CLI.
Diagram lapisan
graph TD
CLI["cli/main.py — Typer app"]:::config
CMD["cli/commands/* — init | scan | recon | analyze | report | workflow"]:::config
ENGINE["WorkflowEngine — core/workflow.py"]:::core
SCHEMA["workflow_schema — compile_workflow / CompiledWorkflow"]:::derived
MEMORY["PentestMemory — core/memory.py"]:::storage
SCOPE["ScopeValidator — utils/scope_validator.py"]:::core
AGENTS["BaseAgent — core/agent.py (Planner, ToolAgent, Analyst, Reporter)"]:::core
CLIENT["AIClient — ai/ai_client.py (alias GeminiClient)"]:::abstract
FACTORY["get_provider — ai/providers/__init__.py"]:::abstract
PROVIDERS["Provider in-tree + entry point guardian.providers"]:::success
TOOLBASE["BaseTool — tools/base_tool.py"]:::core
TOOLS["Wrapper tool — tools/*.py via TOOL_REGISTRY"]:::success
REPORTS[(reports/ session_<id>.json, report_<id>.md)]:::storage
CLI --> CMD
CMD --> ENGINE
ENGINE --> SCHEMA
ENGINE --> MEMORY
ENGINE --> SCOPE
ENGINE --> AGENTS
AGENTS --> CLIENT
CLIENT --> FACTORY
FACTORY --> PROVIDERS
AGENTS --> TOOLBASE
TOOLBASE --> TOOLS
ENGINE --> REPORTS
MEMORY --> REPORTS
style CLI fill:#f59e0b,color:#000
style CMD fill:#f59e0b,color:#000
style ENGINE fill:#8b5cf6,color:#fff
style SCHEMA fill:#8b5cf6,color:#fff
style MEMORY fill:#2d7d46,color:#fff
style SCOPE fill:#8b5cf6,color:#fff
style AGENTS fill:#8b5cf6,color:#fff
style CLIENT fill:#f59e0b,color:#000
style FACTORY fill:#f59e0b,color:#000
style PROVIDERS fill:#2d7d46,color:#fff
style TOOLBASE fill:#8b5cf6,color:#fff
style TOOLS fill:#2d7d46,color:#fff
style REPORTS fill:#336791,color:#fff
%% fig: Lapisan arsitektur Guardian
%% desc: CLI memanggil WorkflowEngine, yang mengoordinasi agen, provider AI, dan wrapper tool, lalu menyimpan checkpoint session ke reports/.Lapisan CLI hanya mendaftarkan perintah. cli/main.py membuat satu typer.Typer dan menggabungkan setiap fungsi perintah dari cli/commands/ menjadi subcommand, ditambah dua sub-app kb dan telemetry (cli/main.py:64-831). Callback tingkat atas hanya mencetak banner, sehingga tidak ada logika domain di lapisan ini (cli/main.py:85-932).
Batas tanggung jawab per modul
| Modul | Peran | Titik masuk |
|---|---|---|
cli/main.py | Registrasi perintah, versi, banner | app, main |
cli/commands/workflow.py | Parsing opsi, override provider/model, pemanggilan engine | workflow_command, _run_workflow |
core/workflow.py | Orkestrasi: validasi scope, compile DSL, eksekusi per generasi, checkpoint | WorkflowEngine.run_workflow, run_autonomous |
core/workflow_schema.py | Schema, validator, dan compiler DAG | compile_workflow, CompiledWorkflow |
core/agent.py | Kontrak agen dan penalaran | BaseAgent, think, think_deeply |
core/tool_agent.py | Registry tool dan resolusi lazy | TOOL_REGISTRY, _get_tool |
ai/providers/__init__.py | Registry dan factory provider | PROVIDERS, get_provider |
tools/base_tool.py | Kontrak wrapper tool dan eksekusi subprocess | BaseTool.execute |
core/memory.py | State sesi dan ledger token | PentestMemory, Finding, ToolExecution |
Perintah workflow memvalidasi --provider terhadap daftar lima nama, lalu menyuntikkan nilainya ke config["ai"]["provider"] sebelum engine dibuat (cli/commands/workflow.py:125-1513). Daftar itu lebih sempit daripada registry PROVIDERS yang memuat delapan entri in-tree, termasuk ollama, openai_compatible, dan litellm (ai/providers/init.py:19-29).
⚠️ Override lewat
--providerpadaguardian workflowmenolakollama,openai_compatible, danlitellmmeskipun provider tersebut terdaftar di registry. Untuk memakainya, setai.providerlangsung di berkas konfigurasi.
Peran WorkflowEngine
WorkflowEngine adalah satu-satunya komponen yang memegang state eksekusi. Konstruktornya membuat PentestMemory, ScopeValidator, dan AIClient, lalu menginstansiasi empat agen dengan memori dan client yang sama (core/workflow.py:30-584). Nilai max_steps dibaca dari config["workflows"]["max_steps"] dengan default 20 (core/workflow.py:52-585).
run_workflow memvalidasi target, memuat YAML, mengompilasinya, lalu menjalankan langkah generasi demi generasi (core/workflow.py:64-1326). Batas konkurensi diambil dari pentest.max_parallel_tools dengan default 3 dan diterapkan lewat asyncio.Semaphore (core/workflow.py:97-1027). Setiap langkah menyimpan hasil ke step_results yang sekaligus menjadi konteks Jinja2 untuk langkah berikutnya, dan checkpoint ditulis setelah tiap langkah (core/workflow.py:108-1328).
sequenceDiagram
participant CLI as cli/commands/workflow.py
participant E as WorkflowEngine
participant S as workflow_schema
participant T as ToolAgent
participant B as BaseTool
participant M as PentestMemory
CLI->>E: WorkflowEngine(config, target)
CLI->>E: run_workflow(name)
E->>E: scope_validator.validate_target(target)
E->>E: _load_workflow_doc(name)
E->>S: compile_workflow(doc)
S-->>E: CompiledWorkflow(levels)
loop setiap generation
E->>T: execute_tool(tool_name, target)
T->>B: execute(target, stream_callback)
B-->>T: parsed, raw_output, exit_code
T-->>E: result dict
E->>M: add_tool_execution(ToolExecution)
E->>M: save_state(tmp) lalu os.replace
end
E-->>CLI: status, findings, session_id
%% fig: Siklus satu workflow run
%% desc: Perintah workflow membuat engine, memvalidasi scope, mengompilasi DAG, lalu menjalankan tool per generasi dan menulis checkpoint setelah setiap langkah.Setiap langkah hasil kompilasi dieksekusi oleh _execute_compiled_step, yang mengevaluasi ekspresi when, merender parameters dengan Jinja2, lalu menjembatani ke _execute_step berbentuk v1 (core/workflow.py:675-7439). Nilai yang dikembalikan berbentuk {id, success, command, exit_code, parsed, raw_output} sehingga {{ <id>.parsed.<key> }} pada langkah berikutnya tetap bekerja (core/workflow.py:736-74310).
_save_session menulis session_<id>.json ke direktori output.save_path (default ./reports) melalui berkas sementara di direktori yang sama, lalu os.replace agar penggantian bersifat atomik (core/workflow.py:833-87011). resume_session memuat kembali checkpoint itu dan mengembalikan False bila berkasnya tidak ada (core/workflow.py:872-89212). Detail format DSL dan aturan kompilasi ada di Engine Workflow DSL v2.
Registry provider dan abstraksi client
AIClient membungkus satu provider dan mendelegasikan seluruh panggilan generasi ke objek itu; nama kelas provider dan model dicatat saat inisialisasi (ai/ai_client.py:20-3113). Modul ai/gemini_client.py hanya me-rebind GeminiClient = AIClient demi kompatibilitas, sehingga seluruh agen dan engine tetap mengimpor nama lama (ai/gemini_client.py:6-1114).
get_provider membaca config["ai"]["provider"] dengan default gemini, mencari nama itu di registry, lalu mengimpor kelasnya lewat importlib.import_module dan getattr (ai/providers/init.py:90-109). Nama yang tidak dikenal memunculkan ValueError berisi daftar provider yang tersedia, sedangkan kegagalan impor dibungkus menjadi RuntimeError (ai/providers/init.py:97-119).
graph LR
CFG["config[ai][provider]"]:::config
LOOKUP["get_provider"]:::abstract
MERGE["_discover_plugin_providers"]:::derived
INTREE["PROVIDERS in-tree (8 entri)"]:::success
EP["entry point guardian.providers"]:::derived
IMPORT["importlib.import_module"]:::core
ERR["ValueError: Unknown provider"]:::failure
CFG --> LOOKUP
LOOKUP --> MERGE
INTREE --> MERGE
EP --> MERGE
MERGE -->|nama ditemukan| IMPORT
LOOKUP -->|nama tidak ada| ERR
style CFG fill:#f59e0b,color:#000
style LOOKUP fill:#f59e0b,color:#000
style MERGE fill:#8b5cf6,color:#fff
style INTREE fill:#2d7d46,color:#fff
style EP fill:#8b5cf6,color:#fff
style IMPORT fill:#8b5cf6,color:#fff
style ERR fill:#dc2626,color:#fff
%% fig: Resolusi provider pada factory
%% desc: Factory menggabungkan registry in-tree dengan entry point plugin, lalu mengimpor kelas provider; nama tak dikenal berakhir sebagai ValueError.Penggabungan entry point dilakukan sekali per proses dan di-cache di _discovered_cache. Nama yang sudah ada di PROVIDERS tidak boleh ditimpa plugin: upaya itu dicatat sebagai warning lalu dilewati (ai/providers/init.py:35-73). Kontrak plugin dan daftar provider lengkap dibahas di Provider AI dan Kontrak Plugin.
Lapisan agen
BaseAgent adalah kelas abstrak dengan satu metode wajib execute (core/agent.py:16-3615). Semua penalaran melewati think, yang memanggil generate_with_usage dan mencatat ThinkingStep serta TokenUsage ke memori (core/agent.py:40-6016). think_deeply menambahkan loop kritik diri dan opsi judge_model, yang dibaca dari argumen atau config["ai"]["judge_model"] (core/agent.py:62-9917).
Setiap agen konkret menerima config, gemini_client, dan memory yang sama. PlannerAgent.decide_next_action membangun prompt dari memory.get_context_for_ai(), membungkus temuan dengan wrap_untrusted, lalu mem-parse keputusan (core/planner.py:19-6918). Pembagian peran antar agen, termasuk DebateTriage dan VisualTriage yang dipanggil dari _execute_step, dijelaskan di Sistem Multi-Agen.
Lapisan tool
TOOL_REGISTRY memetakan nama tool ke string module:Class, dan resolusi baru terjadi saat tool pertama kali dipakai sehingga startup tidak mengimpor seluruh wrapper (core/tool_agent.py:15-7919). _discover_plugin_tools menggabungkan registry in-tree dengan entry point guardian.tools dan memberi kelas risiko default active bagi tool plugin (core/tool_agent.py:169-20520). _get_tool mengimpor kelas, menginstansiasinya, dan menyimpannya di cache _instances (core/tool_agent.py:219-23021).
BaseTool memeriksa ketersediaan tool sekali di konstruktor dan menyimpan hasilnya di is_available; bila tidak tersedia, execute mengembalikan hasil skipped alih-alih melempar exception (tools/base_tool.py:24-6922). Eksekusi memakai asyncio.create_subprocess_exec dengan tool_timeout dari pentest.tool_timeout (default 300 detik) dan meneruskan setiap baris keluaran ke stream_callback (tools/base_tool.py:80-11923). Karena execute tidak pernah melempar exception, WorkflowEngine dapat memutuskan lanjut atau lewati langkah. Klasifikasi risiko dan gate konfirmasi diuraikan di Integrasi Tool dan Pemilihan Tool serta Keamanan, Scope, dan Sanitasi.
Perintah sederhana seperti scan melewati engine sepenuhnya: ia memuat konfigurasi, menginstansiasi NmapTool, dan memanggil execute langsung (cli/commands/scan.py:65-7924). Ini batas penting — jalur tool tunggal tidak menulis checkpoint, sehingga --resume hanya berlaku untuk perintah workflow.