NoirPassiveScan
Class methods
Runs every supplied rule against file_content. The only side effect
is recording a rule that raised (see record_rule_failure).
Callers are responsible for pre-filtering by severity (see
filter_rules_by_severity). Returns an empty array (no allocation
beyond the literal) when there are no rules to run, so callers can
short-circuit on passive_scans.empty? before reading the file.
file_content must be valid UTF-8 — the scan reads it through
Noir::TextFile.read — because matching skips PCRE2's per-call
re-validation (Noir::TextFile::MATCH_OPTIONS).
Each matcher keeps its own whole-file pre-check. Folding every rule's patterns into one alternation looks cheaper but measured ~6x slower: the union has no common first byte, so PCRE2 loses the literal-prefix skip that lets each separate matcher jump straight to candidates.
Pass a nil logger to run silently — the detect walk runs this on its
read threads and logs the matches itself, in walk order.
Pre-filter the rule set against min_severity. Callers should run
this once at scan-startup and pass the result into detect per
file, so the per-(file × rule) severity comparison is amortized
down to a single pass over the rule set.
Keep the first rule for each id and drop the rest.
A rule id is the finding's identity: it is what -f json reports, what
SARIF uses as ruleId, and what a CI gate suppresses on. Two rules
sharing an id therefore emitted two findings for the same line under the
same id (SARIF then described both with whichever rule's metadata was
seen first), and nothing said the rule set was inconsistent. This is also
what deduplicates a repeated --passive-scan-path, which used to double
every finding.