Documentation
¶
Overview ¶
Package verify re-runs the module behind a stored finding against its target and reports whether the finding reproduces (`vigolium finding verify`).
A run has two halves. Reconstruct rebuilds what the module originally saw — the module, the baseline request it scanned and the insertion point it mutated — from the finding row: the replay columns first (origin_record_uuid, insertion_point), legacy heuristics second. Run then drives a one-item executor (no store, no dedup, a single module, the point filter) and turns its results into a verdict. Writing the verdict and any status change is the caller's job.
Index ¶
- Constants
- func DefaultModuleResolver(id string) modules.Module
- func LoadExtensionModules(cfg *config.ExtensionsConfig, requester *http.Requester) (map[string]modules.Module, error)
- func MarkStatus(verdict string) string
- func NeedsExtensions(findings ...*database.Finding) bool
- func Record(ctx context.Context, repo *database.Repository, projectUUID string, ...)
- type Baseline
- type HTTPConfig
- type HTTPStack
- type ModuleResolver
- type Plan
- type RecordOptions
- type Result
- type RunOptions
Constants ¶
const ( // BaselineOriginRecord: findings.origin_record_uuid, written by the // executor since schema v3. BaselineOriginRecord = "origin_record" // BaselineLinkedRecord: the finding's linked http_record is a scanned // record (not the finding's own evidence), so it is what the module saw. BaselineLinkedRecord = "linked_record" // BaselineMatchedRecord: a scanned record on the same host, method and path // that differs from the finding's request in exactly one insertion point. BaselineMatchedRecord = "matched_record" // BaselineFindingRequest: the request stored on the finding itself (a // passive finding without a loadable record). BaselineFindingRequest = "finding_request" )
Baseline sources, reported in Plan.Baseline.Source.
const ( PointStored = "stored" // findings.insertion_point PointDiffering = "differing_point" // the one point where baseline and finding request differ )
Insertion-point sources, reported in Plan.InsertionPointSource.
const DefaultTimeout = 2 * time.Minute
DefaultTimeout is the default bound on verifying one finding.
const ExtensionModulePrefix = "ext-"
ExtensionModulePrefix starts the ID of every JS/YAML extension module.
const HTTPTimeout = 15 * time.Second
HTTPTimeout caps one request of a verification; RunOptions.Timeout bounds the whole run.
Variables ¶
This section is empty.
Functions ¶
func DefaultModuleResolver ¶
DefaultModuleResolver looks the id up in the built-in registries.
func LoadExtensionModules ¶
func LoadExtensionModules(cfg *config.ExtensionsConfig, requester *http.Requester) (map[string]modules.Module, error)
LoadExtensionModules loads the JS/YAML extensions cfg names and returns their modules by ID (active first when an ID is both).
func MarkStatus ¶
MarkStatus is the lifecycle status a verdict moves a finding to under RecordOptions.Mark ("" = none).
func NeedsExtensions ¶
NeedsExtensions reports whether any finding was reported by an ext-* module.
func Record ¶
func Record(ctx context.Context, repo *database.Repository, projectUUID string, f *database.Finding, res *Result, opts RecordOptions)
Record stores res's verdict on finding f (scoped to projectUUID) and, under opts.Mark, the status it implies. A failed write is reported in res.Reason; res.Written and res.Status say what landed.
Types ¶
type Baseline ¶
type Baseline struct {
Source string `json:"source"`
RecordUUID string `json:"record_uuid,omitempty"`
Method string `json:"method,omitempty"`
URL string `json:"url,omitempty"`
// Request is the raw baseline request, filled for --dry-run output.
Request string `json:"request,omitempty"`
}
Baseline describes the request the module will be run against.
type HTTPConfig ¶
type HTTPConfig struct {
ProxyURL string
Verbose bool
Debug bool
DumpTraffic bool
// Timeout is the run's bound; one request gets the smaller of it and
// HTTPTimeout.
Timeout time.Duration
// RateLimit is requests/second across the verification (0 = unlimited;
// fractions allowed).
RateLimit float64
}
HTTPConfig configures NewHTTPStack.
type HTTPStack ¶
HTTPStack is the requester a verification runs modules with, behind an egress gate so the request count can be reported. Close releases it.
func NewHTTPStack ¶
func NewHTTPStack(cfg HTTPConfig) (*HTTPStack, error)
NewHTTPStack builds the HTTP stack for a verification. It takes a reference on the process network dialer (network.Init) that Close returns.
type ModuleResolver ¶
ModuleResolver returns the module with the given id, or nil when no such module is loaded. The CLI wraps DefaultModuleResolver to add ext-* modules loaded from --ext.
func Resolver ¶
func Resolver(loadExt func() map[string]modules.Module) ModuleResolver
Resolver returns a ModuleResolver over the built-in registries plus ext, asking loadExt for the extension modules on the first ext-* lookup only. A nil loadExt (or one returning nil) leaves ext-* modules unresolved, which Reconstruct reports as unsupported.
type Plan ¶
type Plan struct {
FindingID int64 `json:"id"`
FindingHash string `json:"finding_hash"`
ModuleID string `json:"module_id"`
ModuleType string `json:"module_type,omitempty"` // active | passive
Baseline *Baseline `json:"baseline,omitempty"`
InsertionPoint *database.FindingInsertionPoint `json:"insertion_point,omitempty"`
InsertionPointSource string `json:"insertion_point_source,omitempty"`
Payload string `json:"payload,omitempty"`
// Network is true when running the plan sends requests to the target.
Network bool `json:"network"`
// Verdict is set when reconstruction already decides the outcome
// (unsupported, or inconclusive for lack of a baseline); Run returns it
// without executing anything.
Verdict string `json:"-"`
Reason string `json:"-"`
// contains filtered or unexported fields
}
Plan is a reconstructed finding: what verify will run, or why it will not.
func Reconstruct ¶
func Reconstruct(ctx context.Context, repo *database.Repository, projectUUID string, f *database.Finding, resolve ModuleResolver) *Plan
Reconstruct rebuilds the plan for one finding. repo may be nil, in which case only the request stored on the finding is available.
func (*Plan) NeedsNetwork ¶
NeedsNetwork reports whether running p sends requests: an active module, or a passive one asked to refetch.
type RecordOptions ¶
type RecordOptions struct {
// Mark also moves the lifecycle status: not_reproduced → false_positive,
// confirmed → confirmed, through the audited status path.
Mark bool
// Actor is recorded on a status change Mark makes.
Actor string
// ModuleVersion is stored with the verdict (the binary's version).
ModuleVersion string
}
RecordOptions configures Record.
type Result ¶
type Result struct {
ID int64 `json:"id"`
FindingHash string `json:"finding_hash"`
ModuleID string `json:"module_id"`
Verdict string `json:"verdict"`
Reason string `json:"reason,omitempty"`
Plan *Plan `json:"plan,omitempty"`
// Results counts what the re-run module reported; Matched how many of
// those are this finding.
Results int `json:"results"`
Matched int `json:"matched"`
Requests int64 `json:"requests"`
DurationMs int64 `json:"duration_ms"`
// Written is true once the verdict is stored on the finding; Status is the
// status change --mark made, when it made one.
Written bool `json:"written"`
Status *database.FindingStatusChange `json:"status_change,omitempty"`
}
Result is one finding's verify outcome: the JSON item `finding verify -j` emits.
type RunOptions ¶
type RunOptions struct {
// Requester and Services carry the HTTP stack. Services may be nil; a
// fresh dedup manager is used per run either way so one finding's
// module-internal dedup never hides another's.
Requester *http.Requester
Services *services.Services
// Refetch makes a passive verify fetch a fresh response instead of
// re-reading the stored one (the default sends nothing).
Refetch bool
// Timeout bounds one finding's run (0 = no bound beyond the module's own).
Timeout time.Duration
}
RunOptions configures Run.