11
Keamanan, Scope, dan Sanitasi
Guardian memeriksa tiga hal di jalur eksekusi: apakah target boleh disentuh, apakah konten yang masuk ke prompt sudah bersih, dan apakah tool berisiko boleh berjalan. Pemeriksaan pertama dan kedua hidup di utils/, sedangkan yang ketiga menempel pada WorkflowEngine. Karena ketiganya independen, satu lapis yang longgar tidak otomatis membuka lapis lain.
Perjalanan target dari input sampai eksekusi
WorkflowEngine.__init__ membuat satu ScopeValidator untuk seluruh sesi (core/workflow.py:30-381). Validasi dijalankan sebelum workflow dimuat, baik pada run_workflow (core/workflow.py:80-842) maupun pada run_autonomous (core/workflow.py:162-1653). Kegagalan berhenti sebagai ValueError(f"Invalid target: {reason}"), sehingga tidak ada tool yang sempat berjalan.
stateDiagram-v2
[*] --> validate_target
validate_target --> urlparse : strip lalu urlparse
urlparse --> _is_blacklisted
_is_blacklisted --> Blocked : IP literal atau alias localhost
_is_blacklisted --> _resolve : bukan IP literal
_resolve --> _is_blacklisted : setiap IP hasil DNS
_resolve --> Blocked : None berarti fail-closed
_is_blacklisted --> require_scope_file : lolos blacklist
require_scope_file --> _is_authorized : bernilai true
require_scope_file --> Allowed : bernilai false
_is_authorized --> Allowed : host cocok
_is_authorized --> Blocked : host tidak cocok
Allowed --> _confirm_tool
_confirm_tool --> Blocked : destructive saat safe_mode
_confirm_tool --> [*] : passive atau --yes atau confirm
_confirm_tool --> [*] : declined atau tanpa TTY
Blocked --> [*]
style validate_target fill:#f59e0b,color:#000
style urlparse fill:#8b5cf6,color:#fff
style _is_blacklisted fill:#336791,color:#fff
style _resolve fill:#8b5cf6,color:#fff
style _is_authorized fill:#8b5cf6,color:#fff
style require_scope_file fill:#f59e0b,color:#000
style _confirm_tool fill:#f59e0b,color:#000
style Allowed fill:#2d7d46,color:#fff
style Blocked fill:#dc2626,color:#fff
%% fig: Perjalanan target dari validasi sampai gate eksekusi
%% desc: Target melewati blacklist, resolusi DNS, allowlist opsional, lalu confirmation gate sebelum satu tool dijalankan.validate_target mengembalikan pasangan (bool, Optional[str]); urutan pemeriksaannya tetap dan berhenti pada kegagalan pertama (utils/scope_validator.py:87-1154).
| Urutan | Pemeriksaan | Sumber | Perilaku saat gagal |
|---|---|---|---|
| 1 | literal IP atau alias localhost diuji terhadap blacklist_networks | utils/scope_validator.py:117-1645 | False + SCOPE_VIOLATION CRITICAL |
| 2 | hostname di-resolve, setiap IP hasil DNS diuji | utils/scope_validator.py:151-1636 | False + SCOPE_VIOLATION CRITICAL |
| 3 | resolusi DNS gagal | utils/scope_validator.py:143-1497 | False — fail-closed |
| 4 | scope.require_scope_file aktif dan host di luar allowlist | utils/scope_validator.py:108-1138 | False + SCOPE_VIOLATION HIGH |
Blacklist CIDR berlapis
ScopeValidator selalu menggabungkan CIDR dari config dengan entri _HARDCODED_BLACKLIST_CIDRS; entri config tidak bisa menghapus entri bawaan (utils/scope_validator.py:36-499). Config bawaan hanya mendaftarkan empat CIDR, sehingga enam entri sisanya murni berasal dari kode (config/guardian.yaml:102-11510).
| CIDR | Cakupan |
|---|---|
127.0.0.0/8 | loopback IPv4 |
10.0.0.0/8 | RFC1918 |
172.16.0.0/12 | RFC1918 |
192.168.0.0/16 | RFC1918 |
169.254.0.0/16 | link-local, termasuk metadata 169.254.169.254 |
100.64.0.0/10 | carrier-grade NAT |
0.0.0.0/8 | alamat yang tidak sah sebagai tujuan |
::1/128 | loopback IPv6 |
fc00::/7 | unique-local IPv6 |
fe80::/10 | link-local IPv6 |
Sepuluh entri itu ada di utils/scope_validator.py:19-3011. Perbandingan menyamakan versi alamat lebih dulu lewat ip.version == network.version, jadi entri IPv4 tidak salah menolak alamat IPv6 (utils/scope_validator.py:131-13712).
⚠️ Resolusi DNS di-cache per hostname (utils/scope_validator.py:166-18213). Saat
getaddrinfogagal, cache diisi daftar kosong dan_resolvemengembalikanNonepada panggilan pertama (utils/scope_validator.py:176-17914); panggilan berikutnya membaca daftar kosong itu dari_dns_cachesehingga tidak lagi melewati cabang fail-closed (utils/scope_validator.py:143-1497). Nilailah hasil validasi pertama, bukan yang kedua, ketika memutuskan satu host.
Allowlist dan scope file
load_scope_file membaca satu target per baris, melewati baris kosong dan komentar #, lalu memisahkan IP/CIDR dari domain (utils/scope_validator.py:59-8515). _is_authorized menerima IP, network, domain persis, wildcard *.example.com, dan awalan titik yang mencakup subdomain (utils/scope_validator.py:184-21716); aturan wildcard-nya ada di utils/scope_validator.py:200-21517.
| Kunci config | Nilai bawaan | Dibaca di |
|---|---|---|
scope.blacklist | 4 CIDR | utils/scope_validator.py:36-499 |
scope.require_scope_file | false | utils/scope_validator.py:108-1138 |
scope.max_targets | 100 | tidak dirujuk di utils/scope_validator.py |
⚠️ Dengan
require_scope_file: false(config/guardian.yaml:102-11510), pemeriksaan allowlist dilewati sepenuhnya dan hanya blacklist yang aktif. Mengaktifkannya membuat setiap target di luar berkas scope ditolak sebagaiSCOPE_VIOLATIONHIGH (utils/scope_validator.py:108-1138).
add_authorized_target menambah IP, network, atau domain ke daftar izin tanpa memeriksa blacklist lebih dulu (utils/scope_validator.py:230-24318).
Validasi ulang host yang ditemukan tool
Tool recon dan prober dapat memunculkan host yang tidak pernah diperiksa di awal. _validate_discovered_hosts mengumpulkan kandidat dari parsed output lalu melewatkan setiap kandidat kembali ke validate_target (core/workflow.py:516-54519).
| Sifat | Isi |
|---|---|
| Kunci kandidat | subdomains, alive_hosts, hosts, live_hosts, found_hosts, results, targets (core/workflow.py:516-51920) |
| Kunci host di dalam objek | host, url, ip, address, subdomain (core/workflow.py:528-53021) |
| Efek penolakan | logger.warning berisi maksimal lima host pertama (core/workflow.py:540-54522) |
| Pembatalan workflow | tidak ada — docstring menyebutnya tripwire dan audit log (core/workflow.py:506-51123) |
Sanitasi konten eksternal
Docstring utils/sanitize.py menyebut tiga akibat bila keluaran tool masuk prompt tanpa dibersihkan: parser regex planner bisa menjalankan NEXT_ACTION pilihan penyerang, instruksi override bisa masuk, dan escape ANSI bisa merusak tampilan console (utils/sanitize.py:4-1324).
graph LR
Raw[tool output eksternal] --> strip_control_chars
strip_control_chars --> escape_close[escape UNTRUSTED_CLOSE]
escape_close --> escape_open[escape UNTRUSTED_OPEN]
escape_open --> wrap_untrusted
UNTRUSTED_CONTENT_RULE --> Prompt
wrap_untrusted --> Prompt[blok DATA-ONLY dalam prompt]
style Raw fill:#dc2626,color:#fff
style strip_control_chars fill:#2d7d46,color:#fff
style escape_close fill:#8b5cf6,color:#fff
style escape_open fill:#8b5cf6,color:#fff
style wrap_untrusted fill:#2d7d46,color:#fff
style UNTRUSTED_CONTENT_RULE fill:#f59e0b,color:#000
style Prompt fill:#336791,color:#fff
%% fig: Dua primitif sanitasi sebelum data masuk prompt
%% desc: Keluaran tool dibersihkan, tag penutup dan pembuka dinetralkan, lalu payload dibungkus bersama aturan DATA-ONLY.| Simbol | Perilaku | Sumber |
|---|---|---|
strip_control_chars | hapus escape ANSI serta C0/C1, pertahankan tab, newline, dan carriage return | utils/sanitize.py:53-6325 |
wrap_untrusted | bersihkan lalu bungkus dengan tag pembuka dan penutup | utils/sanitize.py:66-8126 |
UNTRUSTED_OPEN, UNTRUSTED_CLOSE | literal <UNTRUSTED_TOOL_OUTPUT> dan penutupnya | utils/sanitize.py:49-5027 |
UNTRUSTED_CONTENT_RULE | teks aturan DATA-ONLY untuk system prompt | utils/sanitize.py:85-9528 |
wrap_untrusted menetralkan upaya keluar dari kotak dengan mengganti tag yang disisipkan penyerang menjadi entitas HTML sebelum membungkus payload (utils/sanitize.py:78-8029). Test mengunci bahwa hasil akhir hanya memuat satu tag penutup (tests/test_sanitize.py:56-6230), dan seluruh perilaku wrap_untrusted diuji di tests/test_sanitize.py:44-6831.
UNTRUSTED_CONTENT_RULE benar-benar dipakai: template planner menyisipkannya ke system prompt (ai/prompt_templates/planner.py:1932), dan PlannerAgent membungkus findings, attack surface, serta technologies sebelum masuk prompt (core/planner.py:48-5233).
Pembersihan karakter kontrol diuji untuk ANSI CSI, OSC, C0, DEL, dan C1, termasuk sifat idempoten (tests/test_sanitize.py:13-4134).
Confirmation gate
_execute_step memanggil _confirm_tool sebelum tool dijalankan dan menandai step selesai bila permintaan ditolak (core/workflow.py:224-23835). Risiko tool diambil dari TOOL_RISK_CLASS dengan nilai default active untuk tool yang tidak terdaftar (core/workflow.py:444-49636).
| Risiko | safe_mode | require_confirmation | --yes | Hasil |
|---|---|---|---|---|
passive | apa pun | apa pun | apa pun | langsung jalan |
destructive | true | apa pun | apa pun | skip tanpa prompt |
destructive | false | true | false | prompt typer.confirm |
active | apa pun | false | apa pun | langsung jalan |
active | apa pun | true | true | langsung jalan |
active | apa pun | true | false | prompt typer.confirm |
Bawaan config adalah safe_mode: true dan require_confirmation: true (config/guardian.yaml:72-8637).
⚠️ Tool
destructivediblokir disafe_modemeskipun--yesdiberikan; satu-satunya jalan adalah mengubahpentest.safe_modemenjadifalse(core/workflow.py:444-49636).
Bila prompt tidak bisa dijalankan, misalnya karena tidak ada TTY, _confirm_tool memilih skip dan menyarankan --yes untuk eksekusi tanpa pengawasan (core/workflow.py:444-49636). Perilaku scope di balik gate ini dikunci test: skenario DNS bypass diuji di tests/test_scope_validator.py:59-9538 dan blokir IP literal di tests/test_scope_validator.py:20-5639.
Kredensial dan audit trail
log_tool_execution menulis arguments apa adanya dan result_preview sepanjang 200 karakter pertama ke log (utils/logger.py:133-14340). Tool yang menerima kredensial sebagai parameter wajib memask sendiri: tools/kerbrute.py:5941 menulis "password": "***" dan tools/crackmapexec.py:7642 menulis "secret": "***".
| Lokasi | Perlakuan kredensial |
|---|---|
| config | api_key: null, nilai diambil dari environment variable (config/guardian.yaml:10-1743) |
guardian init | Prompt.ask(..., password=True) lalu ditulis ke .env (cli/commands/init.py:50-5844) |
| log tool | dimask oleh tool itu sendiri dengan *** (tools/kerbrute.py:5941) |
log_security_event memetakan severity ke level logging: CRITICAL ke critical, HIGH ke error, MEDIUM ke warning, sisanya info (utils/logger.py:145-16245). Berkas log utama berotasi pada 10 MB dengan 5 generasi, sehingga plafon sekitar 50 MB (utils/logger.py:16-1946).
Perilaku di halaman ini dikunci oleh test; lihat Pengujian dan Evaluasi. Opsi --yes dan alur perintahnya dijelaskan di Referensi Perintah CLI.