verify

package
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package verify holds the deterministic, LLM-free primitives of the autonomous pentester's 4-gate verifier (see docs/AUTONOMOUS-PENTEST.md §5): Gate 1, differential reproduction, and the mechanism behind Gate 3, out-of-band (OOB) confirmation.

Both primitives turn a *candidate* vulnerability into machine-proven ground truth without any LLM in the loop:

  • Gate 1 re-issues a payload request and a matched baseline/control N times and requires a class-specific oracle (reflection, DB error, boolean-length split, or timing) to hold on every attempt — a single flaky observation rejects the candidate.
  • Gate 3's mechanism sends a probe carrying a globally-unique OOB token and polls for a callback to that exact token; a hit is proof the payload executed server-side.

The package is deliberately decoupled from the concrete sender/OOB catcher: it depends only on the small Sender / OOBPoller interfaces defined here, injected by the caller. Real adapters over internal/sender and internal/oob are wired in Phase 2; the unit tests inject scripted fakes, so nothing here touches the network or the wall clock. Every class oracle is an exported pure function.

Index

Constants

View Source
const (
	GateDifferential = "differential" // Gate 1
	GateAgent        = "agent"        // Gate 2
	GateOOB          = "oob"          // Gate 3
	GateHuman        = "human"        // Gate 4
)

Gate name constants — the values RejectedAt takes and the keys of the Gates map.

Variables

This section is empty.

Functions

func BooleanLengthHeld

func BooleanLengthHeld(baseline, payloadTrue, payloadFalse Exchange) bool

BooleanLengthHeld models the two-payload boolean-SQLi oracle: a TRUE condition response should match the baseline length while a FALSE condition diverges clearly from the TRUE response. Guards:

  • baseline must be non-trivial (>= booleanBaselineFloor bytes) — on tiny bodies natural variation reads as a large relative divergence (false positives), the same floor the active-scan boolean check applies;
  • all three exchanges must have completed;
  • true ≈ baseline within a small tolerance, AND false differs from true beyond a larger floor.

Pure; the three Exchange bodies are supplied by the caller.

func ErrorSignatureHeld

func ErrorSignatureHeld(baseline, payload Exchange) bool

ErrorSignatureHeld reports whether a DB/interpreter error signature appears in the payload response but not in the baseline. Requiring absence in the baseline avoids flagging an endpoint that always echoes such a string. Pure.

func ReflectedMarkerHeld

func ReflectedMarkerHeld(baseline, payload Exchange, marker string) bool

ReflectedMarkerHeld reports whether a unique marker is present in the payload response but absent from the baseline — the reflected-XSS/injection oracle. The marker must be non-empty; an empty marker never "reflects" (guards against a vacuously-true match). Pure and directly testable.

func TimingHeld

func TimingHeld(baseline, payload, control Exchange) bool

TimingHeld reports whether the timing oracle holds: the payload response is at least timingSlowMs, while both a zero-delay control and the baseline are fast (below timingFastMs) and actually completed. A blocked/errored control returns DurMs 0 and would falsely pass a naive "<threshold" test, so the control and baseline must both be ok(). Pure.

Types

type AgentVerifier

type AgentVerifier interface {
	Disprove(ctx context.Context, c Candidate, evidence DiffResult) Verdict
}

AgentVerifier is Gate 2: an adversarial verifier agent, given the candidate plus the Gate-1 differential evidence and told to disprove it. The concrete impl is built in Phase 2 over internal/aiagent; tests inject a scripted fake.

type Candidate

type Candidate struct {
	VulnClass string   // e.g. "sqli-boolean","xss-reflected","ssrf-blind","cmdi-time"
	Severity  Severity // drives whether Gate 4 applies
	Target    string   // url/endpoint
	Point     string   // injection point description
	Blind     bool     // requires OOB proof (Gate 3)
	Diff      DiffSpec // for Gate 1
	OOB       *OOBSpec // for Gate 3 when Blind; nil for non-blind classes
	Summary   string   // human-readable candidate description for the agent/human gates
}

Candidate is everything the verifier needs to prove one candidate vulnerability. It carries the class-specific differential (Gate 1) and, for blind classes, the OOB spec (Gate 3), plus enough human-readable context for the agent and human gates. A Candidate is never a Finding: it only becomes one if Verify proves it.

type ConfirmResult

type ConfirmResult struct {
	Confirmed bool
	By        string
	Note      string
}

ConfirmResult is Gate 4's answer: a human's one-click decision for Critical/High candidates before filing.

type Confirmer

type Confirmer interface {
	Confirm(ctx context.Context, c Candidate, proof Proof) ConfirmResult
}

Confirmer is Gate 4: a human confirm for Critical/High candidates. The concrete impl is built in Phase 2 over the humaninput surface; tests inject a fake.

type DiffResult

type DiffResult struct {
	Reproduced   bool    // oracle held N consecutive times
	Times        int     // how many consecutive passes were observed (== N on success)
	Baseline     []int64 // recorded baseline/control flow ids gathered across attempts
	PayloadFlows []int64 // recorded payload flow ids gathered across attempts
	Detail       string  // human/AI-readable explanation
}

DiffResult is the outcome of ReproduceDifferential.

func ReproduceDifferential

func ReproduceDifferential(ctx context.Context, s Sender, spec DiffSpec) DiffResult

ReproduceDifferential re-issues the class's requests up to N times and requires the class oracle to hold on *every* attempt; the first failure short-circuits and rejects the candidate (fluke rejection). It respects ctx cancellation: if ctx is done, it stops and returns a not-reproduced result. Deterministic given a deterministic Sender — no real network, no sleeps.

type DiffSpec

type DiffSpec struct {
	Class    VulnClass
	Baseline Request
	Payload  Request // the confirming payload (reflected / error / timing)

	// Boolean-length variant.
	PayloadTrue  Request
	PayloadFalse Request

	// Timing variant: a zero-delay control that must return fast.
	Control Request

	// Marker is the unique string the reflected-marker oracle looks for.
	Marker string

	// N is how many consecutive times the oracle must hold. Defaults to 3 when
	// <= 0 (see effectiveN).
	N int
}

DiffSpec describes a differential-reproduction attempt (Gate 1). It carries the requests needed by the chosen oracle plus how many consecutive passes are required. The set of requests used depends on Class:

  • ClassReflected / ClassError: Baseline + Payload (Marker for reflected).
  • ClassBoolean: Baseline + PayloadTrue + PayloadFalse.
  • ClassTiming: Baseline + Payload + Control (zero-delay).

type Exchange

type Exchange struct {
	Status  int
	Headers map[string][]string
	Body    []byte
	DurMs   int64
	FlowID  int64 // the recorded flow, for PoC attachment later
	Err     error
}

Exchange is a single HTTP exchange the verifier can reason about, kept minimal and provider-agnostic so a real sender adapter and a test fake can both produce it. It captures only what the oracles need (status, headers, body, duration) plus the recorded FlowID for later PoC attachment and any transport Err.

type OOBPoller

type OOBPoller interface {
	HitsForToken(token string) int
}

OOBPoller answers "did a callback arrive for this token?". Phase 2 injects an adapter over internal/oob.Catcher (counting interactions whose token matches); tests inject a fake that "arrives" after k polls.

type OOBResult

type OOBResult struct {
	Confirmed bool   // a callback for Token arrived within the window
	Token     string // echoed for the proof record
	ProbeFlow int64  // the recorded probe flow id (for PoC attachment)
	Polls     int    // how many times the poller was queried
	Detail    string
}

OOBResult is the outcome of ConfirmOOB.

func ConfirmOOB

func ConfirmOOB(ctx context.Context, s Sender, poll OOBPoller, spec OOBSpec) OOBResult

ConfirmOOB sends the probe once, then polls poll.HitsForToken(token) until a hit arrives or the window elapses. A callback to a globally-unique URL only we injected is proof the payload executed server-side, so a single hit confirms. No hit within the window ⇒ not confirmed (blind candidates are never filed on inference alone). Respects ctx cancellation: a cancelled ctx stops polling and returns not-confirmed.

The poll cadence is fully injectable (Window/Interval + a fake sleep), so tests drive it without real time.Sleep. The number of poll iterations is deterministic: ceil(window/interval)+1 (an immediate check, then one per interval across the window).

type OOBSpec

type OOBSpec struct {
	Probe    Request       // request with the token URL already injected
	Token    string        // the exact token to correlate an interaction against
	Window   time.Duration // total time to wait for a callback (default 30s)
	Interval time.Duration // gap between polls (default 500ms)
	// contains filtered or unexported fields
}

OOBSpec describes an out-of-band confirmation attempt (Gate 3 mechanism). The Probe request must already carry the token's callback URL injected as its payload (the caller mints the token and builds the URL); ConfirmOOB does not construct payloads, it only sends and correlates.

type Proof

type Proof struct {
	Proven       bool
	RejectedAt   string // "" if proven, else GateDifferential|GateAgent|GateOOB|GateHuman
	ReproCount   int
	OOBToken     string
	BaselineFlow int64
	PayloadFlow  int64
	Confidence   int            // 0-100, derived from which gates a class required AND passed
	Gates        map[string]any // per-gate detail for finding_verification.gates
	AgentVerdict Verdict
	HumanConfirm ConfirmResult
}

Proof is the machine proof-record the verifier emits for one candidate. It is the source Phase 2 serializes into a store.FindingVerification row (via ToVerification / Verification): Proven says whether the candidate cleared every applicable gate; RejectedAt names the first gate that failed (empty when proven); Gates carries per-gate detail JSON-serializable straight into finding_verification.gates.

func Verify

func Verify(ctx context.Context, c Candidate, d VerifyDeps) Proof

Verify runs the 4-gate verifier over one candidate in cost order. Any gate that fails rejects immediately: Verify returns Proven=false with RejectedAt naming that gate, so an unproven candidate never becomes a Finding. Gates run in this order (later gates only run if all earlier ones held):

  • Gate 1 (always): differential reproduction. Not reproduced ⇒ reject.
  • Gate 2 (always): adversarial agent. Verdict != "real" ⇒ reject.
  • Gate 3 (only if Candidate.Blind): OOB callback. Not confirmed ⇒ reject.
  • Gate 4 (only if Severity >= High): human confirm. Declined ⇒ reject.

Confidence is derived from which gates the candidate required and passed (see confidence()). ctx cancellation surfaces as the current gate failing (the underlying primitives return not-reproduced / not-confirmed on a cancelled ctx).

func (Proof) ToVerification

func (p Proof) ToVerification(findingID, runID int64, vulnClass, oobToken string) map[string]any

ToVerification is the map form of Verification, for callers that serialize the proof-record generically (e.g. straight into a JSON body or a generic store upsert). Keys match store.FindingVerification's JSON tags so Phase 2 can decode them into the struct without a manual field map.

func (Proof) Verification

func (p Proof) Verification(findingID, runID int64, vulnClass, oobToken string) Verification

Verification builds the persistable proof-record fields for this proof. It does not import internal/store; Phase 2 constructs the store row from the returned struct. oobToken overrides the proof's recorded token when non-empty (the caller may know the run-scoped token); otherwise Proof.OOBToken is used.

type Request

type Request struct {
	Method  string
	URL     string
	Headers map[string][]string
	Body    []byte
}

Request describes a request variant to (re-)issue. It mirrors the shape of internal/sender.Request but is redeclared here to keep the package free of a hard dependency on that concrete type; the Phase-2 adapter maps between them.

type Sender

type Sender interface {
	Send(ctx context.Context, req Request) Exchange
}

Sender re-issues a request variant and returns the recorded exchange. Phase 2 injects a real adapter over internal/sender; tests inject a scripted fake.

type Severity

type Severity int

Severity ranks a candidate's impact. It drives whether Gate 4 (human confirm) applies: only High and Critical candidates require a human touchpoint before filing; Medium/Low/Info auto-file once the machine gates pass.

const (
	SeverityInfo Severity = iota
	SeverityLow
	SeverityMedium
	SeverityHigh
	SeverityCritical
)

func ParseSeverity

func ParseSeverity(s string) Severity

ParseSeverity maps a case-insensitive severity name to a Severity. Unknown or empty strings fall back to SeverityInfo (the safest default: it never triggers the human gate on its own, but such a candidate still needs Gates 1-2).

func (Severity) String

func (s Severity) String() string

String renders the canonical lowercase name.

type Verdict

type Verdict struct {
	Result    string // real | refuted | uncertain
	Reasoning string
}

Verdict is Gate 2's answer: an adversarial verifier agent told to *disprove* the candidate. Result is one of "real" | "refuted" | "uncertain"; anything but "real" rejects.

type Verification

type Verification struct {
	FindingID    int64
	RunID        int64
	VulnClass    string
	Gates        string // JSON of Proof.Gates
	ReproCount   int
	OOBToken     string
	BaselineFlow int64
	PayloadFlow  int64
	Confidence   int
}

Verification mirrors the persistable fields of store.FindingVerification without importing internal/store (keeping this package free of that coupling). Phase 2 maps it 1:1 onto a store.FindingVerification row. Gates is the JSON-encoded per-gate detail (Proof.Gates marshaled); the numeric/string fields are copied straight across.

type VerifyDeps

type VerifyDeps struct {
	Sender Sender
	OOB    OOBPoller
	Agent  AgentVerifier
	Human  Confirmer
}

VerifyDeps injects the four gate collaborators. Sender/OOB drive the two deterministic primitives (Gate 1 / Gate 3); Agent/Human are the two LLM/human gates. The OOB poll cadence is injected on the Candidate's OOBSpec (its sleep hook), so no separate clock is needed here — Verify itself never sleeps.

type VulnClass

type VulnClass string

VulnClass names the differential oracle to apply. Kept as string constants so callers (and the Phase-2 verifier) can pass a plain candidate class through.

const (
	ClassReflected VulnClass = "reflected-marker" // marker echoed in the payload response
	ClassError     VulnClass = "error-signature"  // interpreter/DB error surfaced by the payload
	ClassBoolean   VulnClass = "boolean-length"   // true≈baseline, false diverges (two-payload)
	ClassTiming    VulnClass = "timing"           // payload slow, control + baseline fast
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL