Files
2026-07-13 19:38:35 -05:00

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 full stats.do body
  • 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 curl command per responding endpoint, matching the existing Reproduction shape
  • Hints: diagnostic disclosures map to infrastructure (MID server / host data) or stay review_required when the content is unclassified

How to submit a module

  1. 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.
  2. 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.
  3. 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.Usage block.
  4. 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.
  5. Evidence-based, no false-positive inflation. A finding requires real evidence the surface is exposed. Ambiguous signals belong in inferred or review_required, never observed. New content regexes must not match benign data (see the credential/infrastructure patterns in classify.go for the bar — bare keywords and filename-like strings do not qualify).
  6. Tests are required. Mock the HTTP responses (roundTripFunc / fakeClient pattern) and assert both the positive finding and the negative (no false positive on benign or error responses). Run go vet ./..., go test -race ./... -count=1, and gofmt -w before submitting.
  7. 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 vet and gofmt are 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.