module
Noir::CLI::ScanCommand
Overview
noir scan [paths...] [flags]
Discovers endpoints across one or more code bases. Positional paths
augment any -b PATH flags so both v0 and v1 invocation styles work:
noir scan ./app # v1 positional noir scan ./api ./worker # v1 multi-path positional noir -b ./app # v0 (router default-routes to scan) noir scan -b ./app --passive # v1 explicit + flags
Defined in:
cli/commands/scan.crConstant Summary
-
PROMPT_OVERRIDE_SETTERS =
{"override_filter_prompt" => ->(v : String) do LLM::PromptOverrides.filter_prompt = v end, "override_analyze_prompt" => ->(v : String) do LLM::PromptOverrides.analyze_prompt = v end, "override_bundle_analyze_prompt" => ->(v : String) do LLM::PromptOverrides.bundle_analyze_prompt = v end, "override_llm_optimize_prompt" => ->(v : String) do LLM::PromptOverrides.llm_optimize_prompt = v end} -
TECH_FLAG_SPELLINGS =
{"-t/--techs" => ["-t", "--techs"], "--only-techs" => ["--only-techs"], "--exclude-techs" => ["--exclude-techs"]} -
Every spelling of each tech-selection flag. They all store a comma-separated string whose "never passed" value is
""— the same string--flag ''produces, and the same one the generated config template ships — so the blank case can only be read off argv.--only-techs ''was the sharpest of these: it asked to restrict the scan and silently widened it back to everything, which is what a CI job doing--only-techs "$TECHS"gets when$TECHSis unset. -
WARNING_COLOR =
Colorize::Color256.new(208) -
ANSI 256-color orange used for the protocol-missing warning. Kept as a named constant so the call site reads as "warning color" rather than a bare magic number.
Class Method Summary
-
.cli_flag_names(argv : Array(String)) : Set(String)
Long-flag tokens present on the command line, with
--flag=valuereduced to--flag. -
.glob_error(pattern : String) : String | Nil
Returns nil for a usable glob, otherwise the reason it is broken.
-
.host_error(host : String | Nil) : String | Nil
Why
-uneeds an authority, not just a scheme: it is the base URL every discovered path is appended to. - .run(argv : Array(String))
-
.scan_exit_code(app : NoirRunner, app_diff : NoirRunner | Nil) : Int32
Exit code for a scan that produced its report.
Class Method Detail
Long-flag tokens present on the command line, with --flag=value
reduced to --flag. The only question asked of it is "did the user
type this flag?", which is why the values are dropped.
Returns nil for a usable glob, otherwise the reason it is broken.
Both malformations are found by walking the pattern rather than by
asking File.match?. Crystal's matcher is lazy: it only raises
BadPatternError for an unterminated [ once it actually reaches
that [, and it stops at the first literal that does not match the
subject. Probing with one fixed string therefore reported an
unterminated character set for a[b (whose a happens to be a prefix
of nothing in particular) and passed z[b — the identical
malformation — straight through, where it then excluded nothing and
matched no file loudly enough to raise either. Which of the two a user
got depended on the first character of their pattern.
Brace groups have the same problem in the other direction: *.{rb is
accepted by the matcher and then never matches anything.
Inside a character class a brace is an ordinary character, and the
first character of a class is a literal member — []] is the class
containing ], exactly as Crystal's matcher reads it.
Why -u needs an authority, not just a scheme: it is the base URL
every discovered path is appended to. URI.parse is happy without a
host — -u http:// parses with an empty one — and the concatenation
then promotes the first discovered path segment to the authority, so
/a becomes http://a. With --probe or --status-codes that
fires real HTTP requests at a host the user never named.
Whitespace and control characters are rejected for the same reason
normalize_probe_via! rejects a missing host: -u "not a url" and
-u $'http://a\nb/' are accepted today and baked into every endpoint
of the JSON/OAS/Postman output as an unusable URL.
Returns nil when the host is usable, otherwise the reason (phrased to
slot into the -u/--url <reason> in <url> message).
Exit code for a scan that produced its report.
Exit 2 rather than 1 so a CI gate can tell "scan ran, coverage incomplete" from the usage/validation errors that already exit 1.
degraded used to read !app.analyzer_failures.empty? when that list
held tech-level analyzer failures and nothing else, so --strict — whose
documented contract is "exit 2 if any analyzer failed or skipped a
file" — reported green on a scan that lost a whole subtree to an
unlistable directory, dropped every symlinked package, exported nothing,
or ran zero passive rules. Every one of those now feeds the same list, so
the test is unchanged and finally means what it says.