4.5 KiB
Contributing a snowpick module
snowpick ships with a deliberately tight core: it checks unauthenticated data exposure through Service Portal widgets and the Table REST API. Everything else that touches a different ServiceNow surface is a module — an opt-in check behind its own flag, contributed and reviewed independently.
This keeps the default scan fast and focused, and gives the exposure each module finds a clear home rather than bolting unrelated probes onto the core path.
What belongs in core vs. a module
Core (already shipped, not a module):
- Service Portal widget probing (
/api/now/sp/widget/{id}) - Table REST API probing (
/api/now/table/{table}) - Dynamic widget/table discovery, hints, count-oracle detection, reproduction output
A module is the right shape when the check:
- Targets a ServiceNow surface the core does not cover (a different endpoint family, protocol, or data path)
- Is opt-in behind its own flag, so it never changes the default scan's request volume or output
- Produces findings that fit the existing finding model (status, evidence, reproduction) or extends it cleanly
If a change only adds another widget ID or table/field candidate, that is not a
module — use -widgets-file / -tables-file or a PR to the default lists.
Worked example: diagnostic endpoints (threads.do)
ServiceNow exposes diagnostic endpoints (stats.do, threads.do,
xmlstats.do) that can disclose thread dumps, MID server addresses, internal
stack traces, and semaphore logging when left public. The core already reads
stats.do for a build-tag/version signal, but it does not touch threads.do
or xmlstats.do, nor parse the fuller diagnostic content those endpoints
return.
A "diagnostics" module is the canonical first module:
- Flag:
-diagnostics(opt-in, off by default) - Surface:
GET /threads.do,/xmlstats.do, and the fullstats.dobody - Finding: a new result type (or an extension of the target-level result) recording which endpoints responded, the HTTP status, and parsed signals (build tag, MID server hostnames, thread count)
- Evidence: the same standard as core — a finding is only reported when the endpoint returns diagnostic content, never on a bare 200 to an error page
- Reproduction: a
curlcommand per responding endpoint, matching the existingReproductionshape - Hints: diagnostic disclosures map to
infrastructure(MID server / host data) or stayreview_requiredwhen the content is unclassified
How to submit a module
- Open an issue first. Describe the surface, what it discloses, and why it is distinct from an existing check. This avoids duplicate work and confirms the module boundary before you write code.
- One module per PR. Keep it isolated so it can be reviewed and reverted on its own. Do not bundle core changes or unrelated fixes.
- Gate it behind a flag. The default scan must be byte-for-byte unchanged
when your flag is off. Add the flag to the grouped
flag.Usageblock. - Match the finding model. Reuse
Reproduction, the hint system (observed/inferred/none), and the count/sample distinction where they apply. Do not invent a parallel output format. - Evidence-based, no false-positive inflation. A finding requires real
evidence the surface is exposed. Ambiguous signals belong in
inferredorreview_required, neverobserved. New content regexes must not match benign data (see the credential/infrastructure patterns inclassify.gofor the bar — bare keywords and filename-like strings do not qualify). - Tests are required. Mock the HTTP responses (
roundTripFunc/fakeClientpattern) and assert both the positive finding and the negative (no false positive on benign or error responses). Rungo vet ./...,go test -race ./... -count=1, andgofmt -wbefore submitting. - Document it. Add the flag and a short example (command + redacted output) to the README in the same PR.
Acceptance criteria
A module PR is ready when:
- The default scan is unaffected with the flag off
- All findings are evidence-backed and reproducible via the emitted
curl - Tests cover positive and negative cases and the suite passes clean
go vetandgofmtare clean- The README documents the flag with a real, redacted example
Disclaimer
Modules are subject to the same authorization rules as the tool itself. Only run snowpick and its modules against systems you own or are explicitly authorized to test.