Preset validation#3058
Conversation
📊 Performance Benchmark Report
📈 Detailed Results (All Benchmarks)
🎯 Performance Summary+ 4 improvements 🚀
20 unchanged ✅🔍 Significant Changes (>10%)
🐍 Python Version 3.11.15 |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev #3058 +/- ##
======================================
+ Coverage 91% 91% +1%
======================================
Files 437 441 +4
Lines 37507 38326 +819
======================================
+ Hits 33922 34679 +757
- Misses 3585 3647 +62 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Let's consider replacing the |
Preset validation rework
Replaces silent acceptance of typos and wrong types in BBOT presets with strict, schema-driven validation. Catches mistakes like
modlues: [...],scope: {strct: true},modules: {nucleii: {tgas: "x"}}, andnuclei.mode: aggressiveat config-load time instead of after a multi-hour scan produces nothing.New public API
Validate any preset dict (e.g. from
yaml.safe_load) without instantiating a Scanner:Returns a list of
PresetValidationErrorobjects. Empty list = valid. All errors across all layers are aggregated in a single pass, so a user with five typos sees five errors at once.The pydantic schemas themselves are also exported, for callers that want type-checking, doc generation, or to validate just one layer (e.g. the global config) on its own:
These are validation schemas only — they have no defaults of their own (defaults live in
bbot/defaults.yml). For full preset validation that also covers per-module Config blocks, usevalidate_preset(); the composite schema is built dynamically from the loaded module set, so it isn't a static class.Sample errors
What changed
Dependency: omegaconf → pydantic + pydantic-settings
omegaconfis gone. Configs are now plain dicts merged with a smalldeep_updatehelper, and validation is done by pydantic.pydantic-settingsis the new dep;pyyamlis now an explicit (was transitive) dep.Module schema:
class Config(BaseModuleConfig)Every module's
options = {...}+options_desc = {...}pair has been migrated to a typed pydantic class:BaseModuleConfigcarries the three universal options (batch_size,module_threads,module_timeout), so every module accepts those without redeclaring them. 114 modules migrated via codemod (bbot/scripts/migrate_options_to_config.py); a handful tightened by hand to use proper types where they matter:nuclei.mode: Literal["manual", "technology", "severe", "budget"]baddns.min_severity: Literal["INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"]baddns.min_confidence: Literal["UNKNOWN", "LOW", "MEDIUM", "HIGH", "CONFIRMED"]baddns.custom_nameservers: list[str]deps.behavior: Literal["abort_on_failure", "retry_failed", "ignore_failed", "disable", "force_install"]Composite schema, single-pass validation
ModuleLoader.validation_schemabuilds a composite pydantic model on demand:A single
model_validate()call catches typos at every layer. The schema rebuilds when new module dirs are discovered (chicken-and-egg withmodule_dirsis auto-resolved —validate_presetpreloads any custom dirs declared in the preset before validating).Module Config classes captured via AST + exec
Preload still doesn't import modules (so
bbot -lworks on hosts missing module deps). Theclass Configblock is captured viaast.get_source_segment, thenexec'd at schema-build time in a controlled namespace (Field,BaseModuleConfig,typing.*). Pydantic does the rest. No hand-rolled type whitelist, no annotation-string parsing — anything pydantic understands works (Literal,Union,list[str], etc.).Cleanup
BBOTArgs.exclude_from_validationregex anduniversal_module_optionsdict deleted;BaseModuleConfigcovers their job structurally.BBOTArgs.validate()body shrank from ~12 lines of dotted-path lookup to a 4-linevalidate_preset(...)delegation. CLI typos surface through the same code path that handles preset YAML.bbot/scripts/docs.pyreads universal-option descriptions fromBaseModuleConfig.model_fieldsdirectly — no separate constant to keep in sync.omegaconf-specific test helpers and assertions (OmegaConf.merge,omegaconf.errors.ReadonlyConfigError, dot-attribute access) replaced.Breaking changes
nuclei.moderejects unknown values at validation time, not at scan startup with a warning.modlues,flgas, etc.) raiseValidationErrorinstead of being silently ignored.modules:/output_modules:/exclude_modules:raise with a closest-match suggestion.self.optionsis no longer populated for migrated modules. Modules that read user-supplied values must useself.config.get(...). (This caught two pre-existing bugs inbbot/modules/templates/gitlab.pyandbbot/modules/lightfuzz/lightfuzz.pywhere module behavior was relying onself.optionsinstead ofself.config— both fixed in this PR.)${env:FOO}resolver inside YAML values is gone. Pydantic-settings' native env handling (BBOT_*prefix) is available for whole-field overrides.Files of interest
bbot/core/config/models.py—BBOTConfig,PresetSchema,BaseModuleConfig, sub-models. Schema only — defaults live indefaults.yml.bbot/core/config/merge.py—deep_update,dotted_get/dotted_set. ReplacesOmegaConf.merge/select/from_cli.bbot/core/modules.py—_extract_pydantic_config,_exec_config_class,_build_validation_schema,ModuleLoader.validation_schema.bbot/scanner/preset/validate.py—validate_preset,validate_preset_file,PresetValidationError. Single-pass aggregator with closest-match suggestions.bbot/scripts/migrate_options_to_config.py— codemod that performed the module migration. Idempotent; included for reproducibility.Test plan
test_step_1config/preset/cli/validate tests pass (39/39 in the touched set).gitlab_onprem,virustotal,sslcert,robots,crt,httpx, etc.).bbot --help,bbot -l,bbot -lp,bbot --current-presetall work and produce the same shape of output as before.validate_preset({…})round-trips: known-good presets return[]; presets with seeded typos produce specific, labeled errors.module_dirsdeclared in a preset get preloaded before per-module validation, so user modules aren't falsely flagged.