Skip to content

fix(modules): validate modules at load instead of at match time - #383

Open
TBX3D wants to merge 7 commits into
vmfunc:mainfrom
TBX3D:lane/loader-validation
Open

fix(modules): validate modules at load instead of at match time#383
TBX3D wants to merge 7 commits into
vmfunc:mainfrom
TBX3D:lane/loader-validation

Conversation

@TBX3D

@TBX3D TBX3D commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

stacked on #382, so only the commits above it are this pr's.

six more guards over the same theme, a module that loads clean and then quietly does nothing. unknown matcher types were let through where dns and tcp already reject them, regex compiled lazily at match time and swallowed the error, a type: http module with no http: block only failed at Execute, which a passive scan never reaches, and a typo'd severity ranked against nothing downstream.

the loader itself had two. loadDir aborted the whole walk on one unreadable entry, and LoadAll skipped the embedded set wholesale whenever anything loaded from an on-disk modules/ dir, so running sif from a directory that happens to contain one replaced every builtin. embedded is now the baseline that disk layers over by id.

TBX3D added 7 commits July 30, 2026 19:17
a word matcher with no words (or a regex matcher with no patterns)
evaluates to true under the default AND condition, so it silently
matches every response instead of never matching. this is easy to hit
via a yaml typo: word: instead of words:, a forgotten words: list, or
bad indentation nulling the list. a words: [""] list hits the same bug
through strings.Contains(x, ""), which is always true.

reject both cases in validateMatchers, the shared load-time check
http, dns, and tcp module configs all route through, so the guard
applies to every transport that accepts word/regex matchers.
validateMatchers only special-cased favicon/range/encoding and let any
other type string through, so a typo like "words" for "word" loaded
fine and just never matched (executor default case returns false).
dns and tcp already reject unknown matcher types in their own
validators; give the shared http/dns/tcp path the same allowlist.

also add a permanent test that parses every shipped module under
modules/, so a future load-time guard gets checked against real
modules instead of just fixtures.
regex patterns in matchers and extractors compiled lazily at match
time across http, dns, and tcp, swallowing the compile error and
just skipping the pattern. an invalid regex (a typo, an unbalanced
paren) loaded fine and then silently never matched or extracted,
for the life of the module.

compile every matcher and extractor regex pattern during
ParseYAMLModuleBytes instead, covering http (including request-chain
steps), dns, and tcp, so a bad pattern fails load with the compile
error attached.
ParseYAMLModuleBytes only validated http/dns/tcp/fingerprint config
when its pointer was non-nil, so "type: http" with no http: section
(or a typo'd section name like htttp:) parsed and registered clean;
the mismatch only surfaced at Execute, which a passive scan never
reaches, so the module quietly counted as loaded and did nothing.

require the block matching the declared type. also switch parsing to
KnownFields(true) so a typo'd field inside a present section (e.g.
methdo: for method:) is caught too; verified against all 189 shipped
modules with zero strict-decode failures before enabling it.
LoadAll treated builtins as all-or-nothing: if anything loaded from
the on-disk modules/ dir, the embedded set was skipped entirely.
running sif from a directory that happens to contain a modules/
folder (an unrelated project, a planted one) replaced every builtin
with whatever was on disk, dropping the rest silently.

load the embedded set as the baseline and let the on-disk builtin
dir layer over it: Register already replaces by id, so a disk module
overrides only its own id and every other embedded module survives.

also stop trusting the bare cwd-relative "modules" fallback outside a
sif checkout. it is offered when go.mod in the working directory names
this module (so editing modules/ and re-running the binary still works)
or when there is no embedded fs to fall back on at all; anywhere else
the trusted paths are the directory colocated with the binary, the
embedded fs shipped in it, and the packaged data dirs, so an unrelated
cwd's modules/ folder is not silently treated as a builtin source.
loadDir's filepath.Walk callback returned any per-entry error
(an unreadable file, a directory whose contents can't be listed)
straight through, which aborts the whole walk: every module sorted
after the bad entry never got a chance to load, silently.

log and skip the bad entry instead so the rest of the walk proceeds.
severity was a free-form string copied verbatim into Finding.Severity,
so a typo like "CRTICAL" loaded fine and just never ranked against a
real severity downstream.

reject a severity that names something outside info/low/medium/high/
critical (case-insensitive), matching the levels documented in
docs/modules.md. an empty severity is left alone: plenty of modules
and test fixtures omit it deliberately, which is a separate concern
from catching a typo in one that's actually set.
@TBX3D
TBX3D requested a review from vmfunc as a code owner July 31, 2026 02:30
@github-actions github-actions Bot added modules changes to scan modules tests test changes size/l <500 lines changed labels Jul 31, 2026
@github-actions

Copy link
Copy Markdown

pr summary

7 files changed (+447 -16)

category files
go source 7
tests 3

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 89.85507% with 7 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@a38ba0a). Learn more about missing BASE report.

Files with missing lines Patch % Lines
internal/modules/yaml.go 85.18% 2 Missing and 2 partials ⚠️
internal/modules/loader.go 82.35% 2 Missing and 1 partial ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #383   +/-   ##
=======================================
  Coverage        ?   65.26%           
=======================================
  Files           ?       88           
  Lines           ?     7920           
  Branches        ?        0           
=======================================
  Hits            ?     5169           
  Misses          ?     2355           
  Partials        ?      396           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

modules changes to scan modules size/l <500 lines changed tests test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants