Documentation
¶
Overview ¶
Package probe mints synthetic tokens and verifies consumer behavior against real staging endpoints (PRD §6). Minted tokens are NEVER persisted.
Index ¶
- Constants
- Variables
- func BaselineClaims(iss model.Issuer, c model.Consumer, now time.Time) map[string]any
- func RawUnsignedToken(claims map[string]any) (string, error)
- func SignHS256(secret []byte, claims map[string]any) (string, error)
- func TamperSignature(token string) (string, error)
- type ConsumerOutcome
- type Engine
- func (e *Engine) Run(ctx context.Context, iss model.Issuer, mint MintFunc, ...) ([]model.ProbeResult, []ConsumerOutcome, error)
- func (e *Engine) RunHarness(ctx context.Context, iss model.Issuer, mint MintFunc, ...) ([]model.ProbeResult, []ConsumerOutcome, error)
- func (e *Engine) WithClock(now func() time.Time) *Engine
- type EngineConfig
- type Expectation
- type MintContext
- type MintFunc
- type MutateFunc
- type Probe
Constants ¶
const ( ProbeValidToken = "valid_token" ProbeExpired = "expired" ProbeNotYetValid = "not_yet_valid" ProbeWrongIssuer = "wrong_issuer" ProbeWrongAudience = "wrong_audience" ProbeAlgNone = "alg_none" ProbeAlgConfusion = "alg_confusion" ProbeTamperedSignature = "tampered_signature" ProbeMissingClaim = "missing_required_claim" ProbeRetiredKey = "retired_key" ProbeSiblingClientToken = "sibling_client_token" ProbeHeaderBypass = "header_bypass" ProbeCanaryKey = "canary_key" )
Probe ID constants — stable identifiers referenced by evidence and the API.
Variables ¶
var ErrProbeNotApplicable = errors.New("probe not applicable")
ErrProbeNotApplicable signals that a probe cannot run for a consumer (e.g. no canary key announced, no retired key, no sibling audience). The engine records it as skipped, never as a finding.
Functions ¶
func BaselineClaims ¶
BaselineClaims builds the standard claim set for a minted token (PRD §6.2). Every required claim gets a placeholder value. The returned map is fresh per call so probes can mutate it without cross-contamination.
func RawUnsignedToken ¶
RawUnsignedToken builds a compact JWT with header {"alg":"none"} and an empty signature segment (probe 6). Most JOSE libraries refuse to emit alg=none, so the string is assembled by hand: base64url(header) + "." + base64url(payload) + "."
func SignHS256 ¶
SignHS256 signs claims with HMAC-SHA256 using the given secret (probe 7). The attack presents an RS256 issuer's PUBLIC key PEM bytes as the HMAC secret; a server that naively trusts the token's alg header will verify it.
func TamperSignature ¶
TamperSignature flips the final byte of a compact JWS signature (probe 8), producing a structurally valid but cryptographically invalid token.
Types ¶
type ConsumerOutcome ¶
type ConsumerOutcome struct {
ConsumerID string
Verified bool // baseline valid-token probe passed
Skipped bool // not probeable, host not allowed, or kill switch
Reason string
Results []model.ProbeResult
}
ConsumerOutcome summarizes what happened for one consumer.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine runs probes against consumers.
func NewEngine ¶
func NewEngine(cfg EngineConfig) *Engine
NewEngine constructs an engine with the default probe set.
func (*Engine) Run ¶
func (e *Engine) Run(ctx context.Context, iss model.Issuer, mint MintFunc, consumers []model.Consumer) ([]model.ProbeResult, []ConsumerOutcome, error)
Run probes the applicable consumers and returns all probe results plus a per-consumer outcome summary. iss provides the JWKS key ids; mint signs tokens (the issuer adapter's MintToken).
func (*Engine) RunHarness ¶
func (e *Engine) RunHarness(ctx context.Context, iss model.Issuer, mint MintFunc, consumers []model.Consumer) ([]model.ProbeResult, []ConsumerOutcome, error)
RunHarness fires the generative attack corpus (internal/attack) at each applicable consumer's endpoint. It reuses the same safety machinery as Run — the staging allowlist, response scrubbing, and dry-run — and records one ProbeResult per attack token, keyed "harness:<threat>:<name>". A result with Passed=false whose token expected rejection is a live vulnerability.
type EngineConfig ¶
type EngineConfig struct {
MaxConcurrentPerConsumer int // reserved; probes run sequentially per consumer
MaxConcurrentGlobal int // default 20 — consumers probed in parallel
RequestTimeout time.Duration // default 10s
InterProbeDelay time.Duration // default 200ms
AbortOnConsecutive5xx int // default 3
DryRun bool
// Allowlist of host substrings the engine may target. Empty = deny all
// unless AllowProduction is set (the --i-know-this-is-production override).
Allowlist []string
AllowProduction bool
// KillSwitch is polled before each consumer; returning true stops probing.
KillSwitch func() bool
// MaxRequiredClaimProbes caps probe-9 sub-probes (PRD OPEN-3, default 8).
MaxRequiredClaimProbes int
}
EngineConfig controls probe execution (PRD §6.3).
func DefaultEngineConfig ¶
func DefaultEngineConfig() EngineConfig
DefaultEngineConfig returns the PRD §6.3 defaults.
func (EngineConfig) HostAllowed ¶
func (c EngineConfig) HostAllowed(host string) bool
HostAllowed reports whether the engine may target a host (staging guard). Default deny: an empty allowlist without the production override blocks all. Matching is exact or on a dot-delimited suffix boundary — NEVER a bare substring — so an allowlist of "staging.internal" does not match "staging.internal.attacker.com" or "evil-127.0.0.1.attacker.com".
type Expectation ¶
Expectation encodes what a PASS looks like for a probe.
func (Expectation) Accepts ¶
func (e Expectation) Accepts(status int) bool
Accepts reports whether a status code is a PASS for this expectation.
type MintContext ¶
type MintContext struct {
Issuer model.Issuer
Consumer model.Consumer
// Claims is the baseline claim set (PRD §6.2), fresh per probe invocation.
Claims map[string]any
Now time.Time
Mint MintFunc
// Key IDs resolved from the issuer's JWKS.
ActiveKID string
AnnouncedKID string // canary; empty if none announced
RetiredKID string // empty if none retired
// ActiveKeyPEM is the active key's PEM-encoded public key (probe 7 secret).
ActiveKeyPEM string
// SiblingAudience is another consumer's audience (probe 11); empty if none.
SiblingAudience string
// OmitClaim, when set, is the required claim to drop (probe 9 sub-probes).
OmitClaim string
}
MintContext is passed to a probe's Mutate function. It exposes everything a probe needs to construct its Authorization header without knowing how signing works.
type MutateFunc ¶
type MutateFunc func(ctx MintContext) (authHeader string, extraHeaders map[string]string, err error)
MutateFunc returns the Authorization header value and any extra headers.
type Probe ¶
type Probe struct {
ID string
Name string
Description string
RequiresPrivateKey bool
AppliesTo func(c model.Consumer) bool
Mutate MutateFunc
Expect Expectation
Severity model.Severity
}
Probe is one verification behavior.
func Definitions ¶
func Definitions() []Probe
Definitions returns the 13 probe definitions with their mutation logic (PRD §6.2). Probe 9 (missing_required_claim) is expanded into per-claim sub-probes by the engine; its Mutate drops the single claim named in ctx.OmitClaim.