class ConfigInitializer

Defined in:

config_initializer.cr

Constant Summary

ARRAY_CONFIG_KEYS = ["base", "probe_header", "probe_skip", "probe_match", "set_pvalue", "set_pvalue_header", "set_pvalue_cookie", "set_pvalue_query", "set_pvalue_form", "set_pvalue_json", "set_pvalue_path", "passive_scan_path"] of ::String

Keys whose value should always end up as an Array(YAML::Any) so callers can iterate without per-call type checks.

BOOLEAN_CONFIG_KEYS = ["color", "debug", "verbose", "include_path", "include_techs", "include_callee", "ai_context", "nolog", "no_spinner", "strict", "probe", "#", "legacy", "`send_req`", "is", "migrated", "to", "`probe`", "before", "this", "coercion", "runs", "tls_skip_verify", "all_taggers", "status_codes", "passive_scan", "passive_scan_auto_update", "passive_scan_no_update_check", "ai_agent", "cache_disable", "cache_clear"] of ::String

Keys that should be coerced from a legacy "yes" / "no" string into a real Bool when parsed from config.yaml. Every boolean field in default_options must be listed here; otherwise direct comparisons like options["cache_disable"] == true in scan.cr would miss a legacy cache_disable: yes entry and silently leave the flag off.

CSV_CONFIG_KEYS = ["exclude_path", "exclude_codes", "techs", "only_techs", "exclude_techs", "use_taggers"] of ::String

Keys stored as one comma-separated String, because the CLI flags that feed them accumulate into that shape. A YAML sequence is the natural spelling for a list of globs or tech names — and base: / probe_header: in the same file are sequences — so accept it and join. Untreated, exclude_path: ["*.py"] stringified to Crystal's array inspect (["*.py"]), matched no file and excluded nothing silently; the tech keys at least failed loudly, but with that same inspect string quoted back at the user.

INTEGER_CONFIG_KEYS = ["ai_max_token", "ai_agent_max_steps"] of ::String

Keys whose value must end up an Int, because every consumer reads them through YAML::Any#as_i. A quoted number (ai_max_token: "4000") parses as a String and blew up at the cast — and the generated template itself models the quoted spelling with concurrency: "…", so that is the shape users copy. The equivalent CLI flags run through positive_int_or_die!; these had neither coercion nor validation. Bounds are checked afterwards in Noir::CliValidation, the shared CLI+config gate.

LEGACY_CONFIG_KEY_MAP = {"send_req" => "probe", "send_proxy" => "probe_via", "send_es" => "export_es", "send_with_headers" => "probe_header", "use_matchers" => "probe_match", "use_filters" => "probe_skip"}

v0 config-key → v1 config-key map. Applied during #read_config so a ~/.config/noir/config.yaml written by v0.x with the old deliver/probe keys still loads under v1 without surprises. Mirrors the LEGACY CLI flag aliases in src/options.cr.

Constructors

Instance Method Summary

Constructor Detail

def self.new(override_path : String | Nil = nil) #

override_path is the value of CLI --config-file PATH. When present, ConfigInitializer reads from that file instead of $NOIR_HOME/config.yaml. This makes --config-file flow through the same path as the default config — defaults < file < CLI — so a base: (or any other key) declared in the user's custom config file is actually applied. Pre-fix the --config-file PATH value was only used by validation and a post-CLI merge inside NoirRunner that re-overwrote everything the CLI had just set.


[View source]

Instance Method Detail

def default_concurrency : String #

Default concurrency scales with the host's CPU count, clamped to a safe window. The lower bound of 4 keeps low-core CI runners from serializing on a single worker; the upper bound of 32 keeps channel-synchronisation overhead and (under MT) GC pressure in check on very large boxes. Users who want a specific value still get it via --concurrency N or concurrency: in the config file — those paths overwrite this default.

NOIR_CONCURRENCY lets container/CI pipelines pin the worker count to the pod's CPU allocation without threading a flag through every noir invocation. An explicit --concurrency / config value still wins, since those overwrite this default afterwards.


[View source]
def default_options #

[View source]
def generate_config_file #

[View source]
def read_config #

[View source]
def setup #

[View source]