module Noir::CliValidation

Defined in:

cli_validation.cr

Constant Summary

VALID_OUTPUT_FORMATS = ["plain", "yaml", "json", "jsonl", "toml", "markdown-table", "sarif", "html", "curl", "httpie", "powershell", "oas2", "oas3", "postman", "only-url", "only-param", "only-header", "only-cookie", "only-tag", "mermaid"] of ::String

Class Method Summary

Class Method Detail

def self.exit_with_error(message : String) : NoReturn #

[View source]
def self.validate!(options : Hash(String, YAML::Any)) #

[View source]
def self.validate_ai_provider_pair!(options : Hash(String, YAML::Any)) #

--ai-provider and --ai-model need each other to take effect. Either flag set on its own silently skips the AI analyzer:

  • --ai-provider openai (no model) — ai_provider_active? is false, AI never runs, user just gets a plain scan
  • --ai-model gpt-4 (no provider) — same outcome The ACP family is the one exception: acp:claude / acp:codex carry their own default model, so no --ai-model is required.

[View source]
def self.validate_base_paths!(options : Hash(String, YAML::Any)) #

[View source]
def self.validate_concurrency!(options : Hash(String, YAML::Any)) #

[View source]
def self.validate_config_file!(options : Hash(String, YAML::Any)) #

--config-file PATH needs to exist, be a file (not a directory), and parse as a YAML mapping. The previous shape let File.read / YAML.parse raise straight through to the user, producing a Crystal stack trace for what should be a one-line "wrong path" message.


[View source]
def self.validate_output_format!(options : Hash(String, YAML::Any)) #

[View source]
def self.validate_output_path!(options : Hash(String, YAML::Any)) #

[View source]
def self.validate_passive_scan_paths!(options : Hash(String, YAML::Any)) #

--passive-scan-path PATH accepts multiple entries (repeatable), and each must exist + be a directory — NoirPassiveScan.load_rules walks PATH/**/*.{yml,yaml} and Dir.glob silently returns zero matches for non-existent paths. The result is a passive scan that ran with 0 rules and surfaced 0 findings, with no indication that the path was bogus. Surface the typo at CLI parse time instead.


[View source]
def self.validate_tagger_names!(options : Hash(String, YAML::Any)) #

[View source]
def self.validate_tech_names!(options : Hash(String, YAML::Any)) #

-t/--techs, --only-techs, --exclude-techs should reject unknown tech names eagerly. Pre-fix, a typo like --only-techs falsk (instead of flask) silently dropped through NoirTechs.similar_to_tech returning "" and the scan produced zero endpoints without explanation — indistinguishable from "no flask code found here". Surfacing the typo at CLI parse time saves the surprise.


[View source]
def self.warn_about_unused_delivery_flags(options : Hash(String, YAML::Any)) #

--probe-match and --probe-skip only run inside the Deliver pipeline (the code that ships endpoints to --probe / --probe-via / --export-es / --export-webhook). With no delivery target configured they silently no-op — a real surprise for users who set the flags expecting stdout output to be filtered. Warn at CLI parse time so the gap is obvious.


[View source]