Lompat ke konten
dokumentasi.id
Commit e4397d1
v1
Star2,4 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

2.7

Template YAML dan Plugin Kustom

templates/ dan plugins/ adalah dua jalur perluasan Deep Eye yang tidak berbagi kode. Template YAML bersifat deklaratif: pemeriksaan ditulis sebagai data, divalidasi parser.py, lalu dijalankan TemplateExecutor. Plugin bersifat imperatif: satu atau beberapa kelas Python mewarisi PluginBase, lalu PluginManager memuat dan menjalankannya. Keduanya nonaktif secara default dan baru hidup ketika templates.enabled atau plugin_manager.enabled disetel true. core/scanner_engine.py:174-188 config/config.example.yaml:627-631 docs/CONFIGURATION.md:131-135

Pipeline template dari berkas ke vuln dict

Satu template melewati lima tahap: walk direktori, parse YAML, validasi schema, filter tag/severity, lalu eksekusi. Tahap walk dan filter ada di load_templates; tahap eksekusi ada di TemplateExecutor.run. modules/template_engine/loader.py:11-45 modules/template_engine/executor.py:51-96

graph TD
    dirs["template_directories"] --> walk["load_templates()"]
    walk --> rglob["rglob *.yaml + *.yml"]
    rglob --> parse["parse_template_file()"]
    parse --> check{"validate_template"}
    check -->|"TemplateError"| skip["Skipping template"]
    check -->|"valid"| filt{"tag / severity filter"}
    filt -->|"gugur"| drop["dibuang"]
    filt -->|"lolos"| store["_loaded_templates"]
    store --> run["TemplateExecutor.run()"]
    run --> subst["substitute_vars()"]
    subst --> send["_send_request()"]
    send --> mat{"evaluate_matchers()"}
    mat -->|"overall False"| nomatch["tidak ada finding"]
    mat -->|"overall True"| extract["run_extractors()"]
    extract --> build["_build_vuln()"]
    build --> out["List[vuln dict]"]
    style store fill:#336791,color:#fff
    style skip fill:#dc2626,color:#fff
    style drop fill:#dc2626,color:#fff
    style nomatch fill:#dc2626,color:#fff
    style out fill:#8b5cf6,color:#fff
    style build fill:#8b5cf6,color:#fff
    style check fill:#f59e0b,color:#000
    style filt fill:#f59e0b,color:#000
    style mat fill:#f59e0b,color:#000
%% fig: Pipeline template dari direktori ke vuln dict
%% desc: Loader memvalidasi dan memfilter tiap berkas YAML, lalu executor mengirim request dan membentuk finding hanya ketika matcher lulus.
Fig. 1: Pipeline template dari direktori ke vuln dictLoader memvalidasi dan memfilter tiap berkas YAML, lalu executor mengirim request dan membentuk finding hanya ketika matcher lulus.

Direktori yang tidak ada tidak melempar error; load_templates mencatatnya di logger.debug lalu pindah ke direktori berikutnya. Berkas yang gagal divalidasi juga tidak menghentikan proses: TemplateError ditangkap dan template dilewati dengan pesan Skipping template {file_path}: {e}. modules/template_engine/loader.py:21-32

ScannerEngine memanggil load_templates dengan tiga argumen dari blok konfigurasi templates: template_directories dengan default ['templates'], tag_filters, dan severity_filter. Filter yang kosong dikirim sebagai None. core/scanner_engine.py:177-188

Filter tag memakai irisan himpunan, yaitu tag_set & tpl_tags, sehingga template lolos bila setidaknya satu tag cocok. Filter severity membandingkan info.severity yang sudah di-lower(). modules/template_engine/loader.py:34-41

Perilaku itu dikunci oleh test loader: subdirektori tetap ter-walk, YAML rusak dilewati tanpa menggagalkan berkas lain, dan direktori yang tidak ada menghasilkan list kosong. tests/test_template_engine.py:257-300

Schema wajib

validate_template menolak template sebelum sempat dieksekusi. Aturannya terkumpul di lima konstanta tingkat modul: REQUIRED_TOP_LEVEL, REQUIRED_INFO, VALID_SEVERITIES, VALID_MATCHER_TYPES, dan VALID_PARTS. modules/template_engine/parser.py:9-13

PemeriksaanAturanPesan error
top levelid, info, http wajib adamissing required field
infoname dan severity wajib adainfo.<key> required
severitysalah satu critical, high, medium, low, infoinvalid severity
httplist tidak boleh kosonghttp must be a non-empty list
blok httptiap blok wajib punya path atau rawrequires 'path' or 'raw'
matchertype harus anggota VALID_MATCHER_TYPESmatchers[j].type invalid

Kolom pesan error mengutip teks yang benar-benar dilempar validate_template. modules/template_engine/parser.py:49-81

parse_template memakai yaml.safe_load, bukan yaml.load, lalu menyimpan path sumber ke data["_source"]. Field itu nanti muncul sebagai template_path di dict vulnerability. modules/template_engine/parser.py:20-37 modules/template_engine/executor.py:131-144

Template bawaan mengikuti schema ini. cors-wildcard.yaml memakai matchers-condition: and dengan dua matcher word yang membaca part: header. templates/misconfig/cors-wildcard.yaml:16-35 env-file-exposure.yaml memasangkan matcher status dengan matcher regex pada body. templates/exposures/env-file-exposure.yaml:14-25

Matcher, extractor, dan substitusi variabel

evaluate_matchers memetakan type matcher ke fungsi di _MATCHERS, membungkus tiap pemanggilan dengan try/except, lalu menggabungkan hasil lewat matchers-condition yang default-nya or. Matcher dengan tipe tak dikenal menghasilkan entri dengan matched: False dan error bernilai unknown matcher. modules/template_engine/matcher.py:101-134

TipeFungsiPerilaku
statusmatch_statusstatus_code harus ada di list status
wordmatch_wordsubstring match, mendukung case-insensitive dan condition and/or
regexmatch_regexre.search, pola rusak dihitung False
sizematch_sizepanjang response.text harus ada di list size
dslmatch_dsleval ekspresi dengan safe_globals, condition default and

Detail tiap fungsi ada di modul matcher. modules/template_engine/matcher.py:26-98

run_extractors baru berjalan setelah matcher lulus. Saat ini hanya tipe regex yang menghasilkan nilai, dan nama default-nya extracted_{i}. modules/template_engine/executor.py:78-89 modules/template_engine/matcher.py:137-159

Sebelum request dikirim, substitute_vars mengganti {{BaseURL}}, {{Hostname}}, dan {{RandomString}}. Placeholder terakhir diisi string acak 8 karakter dan diganti per kemunculan lewat loop while. modules/template_engine/executor.py:22-37

⚠️ match_dsl menjalankan eval(str(expr), safe_globals, {}). safe_globals mengosongkan __builtins__ dan hanya menyediakan len, status_code, body, True, dan False, tetapi template tetap kode yang dieksekusi. Perlakukan direktori template seperti kode: jangan memuat template dari sumber tak terpercaya. modules/template_engine/matcher.py:76-98

Bentuk finding

_build_vuln menormalkan hasil ke dict yang sama dengan modul lain: type dari info.name, severity, url, payload yang dipotong 200 karakter, evidence yang dipotong 500 karakter, cve_references dari classification.cve-id, template_id, dan template_path. modules/template_engine/executor.py:114-144

cvss_score tidak dihitung dari vektor serangan; nilainya diambil dari tabel SEVERITY_TO_CVSS dengan critical 9.5, high 7.5, medium 5.5, low 3.0, dan info 0.0. modules/template_engine/executor.py:13-19 modules/template_engine/executor.py:141

Key type, url, dan severity adalah kontrak yang dipakai lapisan Pelaporan dan Ekspor serta Scan Diff dan Retest, jadi jangan mengubahnya tanpa menyesuaikan keduanya.

Siklus plugin kustom

Plugin adalah kelas Python, bukan data. PluginBase menyediakan metadata name, version, author, dan description, serta menerima http_client dan seluruh config di konstruktor. core/plugin_manager.py:16-35

Identitas plugin dihitung, bukan ditulis: get_plugin_id() mengembalikan cls.__name__.lower().replace('plugin', ''). Konfigurasi per-plugin diambil dari config['plugins'][id], dan is_enabled() membaca enabled dari situ dengan default False. core/plugin_manager.py:35-44

graph TD
    cfg["plugin_manager.enabled"] --> gate{"enabled?"}
    gate -->|"false"| off["load_plugins tidak dipanggil"]
    gate -->|"true"| load["PluginManager.load_plugins()"]
    load --> missing{"plugin_dir ada?"}
    missing -->|"tidak"| mkdir["mkdir parents=True, return 0"]
    missing -->|"ya"| glob["glob *.py, lewati nama berawalan _"]
    glob --> imp["_load_plugin_classes()"]
    imp --> insp["inspect.getmembers, issubclass PluginBase"]
    insp -->|"kosong"| warn["No PluginBase subclass found"]
    insp -->|"ada"| inst["plugin_class(http_client, config)"]
    inst --> reg["self.plugins[plugin_id]"]
    reg --> enable["get_enabled_plugins()"]
    enable --> scan["scan_with_plugins(url, context)"]
    scan --> merge["vulnerabilities.extend(...)"]
    style reg fill:#336791,color:#fff
    style off fill:#dc2626,color:#fff
    style warn fill:#dc2626,color:#fff
    style mkdir fill:#dc2626,color:#fff
    style merge fill:#2d7d46,color:#fff
    style cfg fill:#f59e0b,color:#000
    style enable fill:#f59e0b,color:#000
    style inst fill:#8b5cf6,color:#fff
%% fig: Siklus pemuatan dan eksekusi plugin
%% desc: PluginManager memuat berkas .py hanya ketika enabled, mendaftarkan tiap subclass PluginBase per plugin_id, lalu menjalankan yang enabled saat scan.
Fig. 2: Siklus pemuatan dan eksekusi pluginPluginManager memuat berkas .py hanya ketika enabled, mendaftarkan tiap subclass PluginBase per plugin_id, lalu menjalankan yang enabled saat scan.

load_plugins melewati berkas yang namanya berawalan _, sehingga __init__.py tidak pernah dieksekusi sebagai plugin. Direktori yang belum ada dibuat dengan mkdir(parents=True, exist_ok=True) dan fungsi mengembalikan 0, bukan error. core/plugin_manager.py:87-103

_load_plugin_classes memakai importlib.util.spec_from_file_location, menjalankan modul, lalu menyaring inspect.getmembers ke kelas yang issubclass(obj, PluginBase) dan bukan PluginBase itu sendiri. Satu berkas boleh memuat lebih dari satu plugin; test membuktikan AlphaPlugin dan BetaPlugin di berkas yang sama sama-sama terdaftar. core/plugin_manager.py:118-138 tests/test_bugfix_gaps.py:43-66

Kelas yang tidak mengimplementasi scan akan gagal saat dipanggil, karena PluginBase.scan melempar NotImplementedError dengan pesan Plugin must implement scan() method. scan_with_plugins menangkap exception per plugin, mencatatnya lewat logger.error, lalu melanjutkan plugin berikutnya. core/plugin_manager.py:46-57 core/plugin_manager.py:152-175

plugins/example_plugin.py adalah rujukan bentuk plugin minimal: metadata di tingkat kelas, scan(url, context) yang membaca context.get('response'), lalu dict dengan type, severity, url, evidence, description, remediation, dan plugin. plugins/example_plugin.py:11-63 Bentuk dict itu sama dengan yang didokumentasikan di plugins/README.md. plugins/README.md:103-114

Konfigurasi dan urutan pemanggilan

Enam langkah yang ditulis plugins/README.md cocok dengan kode: berkas diletakkan di plugins/, PluginManager memindai *.py, memuat kelas turunan PluginBase, memeriksa enabled di config, menjalankan plugin saat fase scan, lalu menggabungkan hasil ke laporan akhir. plugins/README.md:124-131

Key konfigurasiDefaultEfek
plugin_manager.enabledfalseScannerEngine memanggil load_plugins() saat konstruksi
plugin_manager.plugin_directorypluginsdirektori yang di-glob *.py
plugin_manager.auto_loadtruetercatat di config contoh
templates.enabledfalseinisialisasi TemplateExecutor dan load_templates
templates.template_directories["templates"]akar walk rglob
templates.tag_filters[]filter tag, dikirim sebagai None bila kosong
templates.severity_filter[]filter severity

Nilai default itu dibaca PluginManager dari config['plugin_manager']['plugin_directory'] dan oleh ScannerEngine dari blok templates. config/config.example.yaml:627-631 core/plugin_manager.py:82-85 docs/CONFIGURATION.md:131-135

Di loop scan, plugin dijalankan lebih dulu lewat scan_with_plugins, lalu template dijalankan dengan template_executor.run(tpl, url) untuk setiap entri _loaded_templates. Keduanya menyumbang ke list vulnerabilities yang sama sebelum pemrosesan pasca-scan. core/scanner_engine.py:532-544

⚠️ Sistem plugin nonaktif secara default dan plugin berjalan dengan akses penuh ke host OS. Jangan mengarahkan plugin_directory ke path world-writable dan jangan memuat plugin dari sumber tak terpercaya. SECURITY.md:48-53

Bagaimana finding dari kedua jalur ini masuk ke laporan dibahas di Pelaporan dan Ekspor; posisi keduanya di dalam satu putaran scan dibahas di Alur Scan dan Pemindai Kerentanan dan Katalog Modul.