Documentation
¶
Index ¶
- Constants
- func BodySimilarity(a, b []byte) float64
- func IsRawHTTPRequest(data []byte) bool
- func NormalizeLoginFlow(lf *LoginFlow)
- func ParseSessionBytes(content []byte, name string, opt ParseOptions) ([]*Session, []LintIssue, error)
- func ResolveCheckURL(checkURL, base string) (string, error)
- func ResolveSessionPath(path, sessionDir string) string
- func ValidateExpect(expect *ExpectResponse, statusCode int, body []byte) error
- type Assessment
- type CheckMatcher
- type Credentials
- type DiscoverResult
- type Duration
- type ExpectResponse
- type ExtractRule
- type ExtractSource
- type LintIssue
- type LiveEvent
- type LiveEventKind
- type LiveOptions
- type LiveSession
- func (s *LiveSession) Close()
- func (s *LiveSession) Credentials() *Credentials
- func (s *LiveSession) Invalidate(gen uint64) bool
- func (s *LiveSession) LoggedOutResponses() int64
- func (s *LiveSession) Name() string
- func (s *LiveSession) Observe(gen uint64, status int, header http.Header, body []byte)
- func (s *LiveSession) Observed() int64
- func (s *LiveSession) OnFailure() ReauthOnFailure
- func (s *LiveSession) State() LiveState
- func (s *LiveSession) Wait(ctx context.Context) error
- type LiveState
- type LoggedInRule
- type LoggedOutRule
- type LoginFlow
- type LoginRequest
- type LoginResult
- type LoginStep
- type LoginType
- type Manager
- func (m *Manager) AllSessions() []*Session
- func (m *Manager) CompareSessions() []*Session
- func (m *Manager) HydrateSessions() error
- func (m *Manager) HydrateSessionsContext(ctx context.Context) error
- func (m *Manager) LoginBudget() time.Duration
- func (m *Manager) Primary() *Session
- func (m *Manager) PrimaryHeaders() []string
- type ManagerOption
- type ParseError
- type ParseOptions
- type ProbeResponse
- type ReauthOnFailure
- type Role
- type Session
- type SessionCheck
- type SessionConfig
- type SessionDefaults
- type SessionDocument
- type SessionForm
- type SessionReauth
- type Signal
- type Verdict
Constants ¶
const ( // LoginReasonUnverified: the flow ran without error but there was no expect // block, and nothing in the response proved the login worked (or the login // form came back). A warning: the session is kept, the command exits 0. LoginReasonUnverified = "unverified" // LoginReasonExpectStatus: an expect.status block rejected the status. LoginReasonExpectStatus = "expect_status_mismatch" // LoginReasonExpectBody: an expect.body_contains block did not match. LoginReasonExpectBody = "expect_body_missing" // LoginReasonUnexpectedStatus: no expect block, and the status was outside // 2xx/3xx. LoginReasonUnexpectedStatus = "unexpected_status" // LoginReasonExtractFailed: an extract rule found nothing. LoginReasonExtractFailed = "extract_failed" // LoginReasonNetworkError: the request did not complete (connect, TLS, // timeout, cancellation, body read). LoginReasonNetworkError = "network_error" // LoginReasonInvalidFlow: the flow could not be built (no login, bad URL, // unparsable login_request). LoginReasonInvalidFlow = "invalid_flow" // Set by callers that report a session without checking it (OK, not // Verified): one with static headers and no login, and one skipped by // `auth load --no-validate`. LoginReasonStaticHeaders = "static_headers" LoginReasonNotValidated = "not_validated" )
Reasons a login check reports. Stable strings: `auth load -j` prints them and a caller branches on them.
const ( DefaultCheckMethod = http.MethodGet DefaultCheckSampleEvery = 50 DefaultReauthMaxAttempts = 3 DefaultReauthBackoff = 10 * time.Second )
Defaults for the check and reauth blocks.
const ( SignalStatusPair = "status_pair" SignalLoginRedirect = "login_redirect" SignalPasswordInput = "password_input" SignalBodySimilarity = "body_similarity" SignalConfiguredLoggedIn = "configured_logged_in" SignalConfiguredLogOut = "configured_logged_out" SignalRequestFailed = "request_failed" SignalInvalidCheck = "invalid_check" )
Signal names.
const ( // OAuth2AuthStyleHeader sends client_id/client_secret as HTTP Basic auth. OAuth2AuthStyleHeader = "header" // OAuth2AuthStyleBody sends them as form fields in the request body. OAuth2AuthStyleBody = "body" )
OAuth2 client authentication styles (login.auth_style).
const DefaultLoginBudget = 2 * time.Minute
DefaultLoginBudget bounds the time ALL login flows together may take. It is a total, not a per-request timeout (each request keeps its own 30s): a scan configured with six sessions, each a three-step flow, could otherwise spend nine minutes in setup before sending a single scan request, with no output and no way to tell a slow login from a hung one. Two minutes is many times what a working login needs and short enough that a wedged one is a visible delay rather than an apparent hang.
Variables ¶
This section is empty.
Functions ¶
func BodySimilarity ¶ added in v0.5.3
BodySimilarity is the Dice coefficient of the two bodies' token multisets, with volatile tokens (numbers, nonces, IDs) masked: 1 means the same words in the same quantities, 0 nothing shared. Two empty bodies are identical.
func IsRawHTTPRequest ¶
IsRawHTTPRequest returns true if the data looks like a raw HTTP request.
func NormalizeLoginFlow ¶
func NormalizeLoginFlow(lf *LoginFlow)
NormalizeLoginFlow expands shorthand type/token_path into explicit extract rules and validates the URL. Called before execution; safe to call multiple times.
func ParseSessionBytes ¶ added in v0.5.3
func ParseSessionBytes(content []byte, name string, opt ParseOptions) ([]*Session, []LintIssue, error)
ParseSessionBytes parses a session file in any accepted form and returns its sessions plus the parse-level issues (unknown fields). name is the file path or a label such as "<stdin>"; a .json suffix selects JSON, otherwise the content is sniffed. ${ENV} references are expanded first.
A file that defines no session is an error here (a loader has nothing to load); ParseSessionDocument returns it as an empty document for lint.
func ResolveCheckURL ¶ added in v0.5.3
ResolveCheckURL resolves the check URL against base: an absolute URL is returned as is, a path is joined to base's origin. base may be any URL on the target (the login URL, a scan target).
func ResolveSessionPath ¶ added in v0.5.3
ResolveSessionPath resolves an --auth-file value the way LoadFromAuthFiles does: a path is used as is, a bare name is looked up in sessionDir (default ~/.vigolium/sessions/) with .yaml, .yml and .json tried in order.
func ValidateExpect ¶
func ValidateExpect(expect *ExpectResponse, statusCode int, body []byte) error
ValidateExpect checks if an HTTP response satisfies the expect constraints.
Types ¶
type Assessment ¶ added in v0.5.3
type Assessment struct {
Verdict Verdict `json:"verdict"`
Reason string `json:"reason"`
Signals []Signal `json:"signals,omitempty"`
// Similarity is the auth/anon body similarity in [0,1], or -1 when it was
// not computed.
Similarity float64 `json:"similarity"`
}
Assessment is AssessAuthenticated's result.
func AssessAuthenticated ¶ added in v0.5.3
func AssessAuthenticated(authResp, anonResp *ProbeResponse, check *SessionCheck) Assessment
AssessAuthenticated decides whether authResp (the URL fetched with the session's credentials) shows a logged-in session, using anonResp (the same request without them) as the baseline. check is the session's check block (nil for none): a configured logged_out match is decisive, a configured logged_in rule overrides the heuristics, and the heuristics are the status pair, a redirect to a login URL, a password input, and body similarity.
func (Assessment) Authenticated ¶ added in v0.5.3
func (a Assessment) Authenticated() bool
Authenticated is Verdict == VerdictAuthenticated.
type CheckMatcher ¶ added in v0.5.3
type CheckMatcher struct {
// contains filtered or unexported fields
}
CheckMatcher is a compiled check block. A nil *CheckMatcher matches nothing (no rules configured).
func (*CheckMatcher) HasLoggedIn ¶ added in v0.5.3
func (m *CheckMatcher) HasLoggedIn() bool
HasLoggedIn reports whether a logged_in rule is configured.
func (*CheckMatcher) HasLoggedOut ¶ added in v0.5.3
func (m *CheckMatcher) HasLoggedOut() bool
HasLoggedOut reports whether a logged_out rule is configured.
func (*CheckMatcher) LoggedIn ¶ added in v0.5.3
func (m *CheckMatcher) LoggedIn(p *ProbeResponse) (bool, string)
LoggedIn reports whether every configured logged_in condition holds on p, and names the first that failed. False when no rule is configured.
func (*CheckMatcher) LoggedOut ¶ added in v0.5.3
func (m *CheckMatcher) LoggedOut(p *ProbeResponse, checkBody bool) (bool, string)
LoggedOut reports whether any logged_out condition matches p, and which ("status", "redirect" or "body"). checkBody=false skips the body regex: the scan's sampler evaluates status and Location on every response and the body only on 1 in sample_every.
type Credentials ¶ added in v0.5.3
type Credentials struct {
// Headers are the headers a request sends for the session: the session's
// own, minus any the operator pinned with an explicit -H.
Headers map[string]string
// Gen numbers the generation, from 1. Each successful re-login adds one.
Gen uint64
// At is when this generation was minted.
At time.Time
// ExpiresAt is when this generation expires, when the login said so (an
// OAuth2 token's expires_in); zero when unknown. The session refreshes
// shortly before it (see expiryLead).
ExpiresAt time.Time
// contains filtered or unexported fields
}
Credentials are one generation of a session's credentials. A published value is never mutated: a re-login publishes a new one.
type DiscoverResult ¶
type DiscoverResult struct {
Session *Session
StatusCode int
TokenSources []string // human-readable descriptions of where tokens were found
RawResponse string // raw response for storage in login_response
}
DiscoverResult holds the output of auto-discovery.
func DiscoverLogin ¶
func DiscoverLogin(loginReq *LoginRequest) (*DiscoverResult, error)
DiscoverLogin sends the given login request and auto-discovers auth tokens from the response. It checks JSON body fields, Set-Cookie headers, and auth response headers. Returns a Session with a LoginFlow populated with auto-generated ExtractRules.
func DiscoverLoginContext ¶ added in v0.5.3
func DiscoverLoginContext(ctx context.Context, loginReq *LoginRequest, client *http.Client) (*DiscoverResult, error)
DiscoverLoginContext is DiscoverLogin bounded by ctx and sent over client's transport (pkg/http.LoginTransport: the scan's proxy and TLS stance). client is copied, never mutated; nil keeps the old direct, certificate-ignoring transport.
type Duration ¶ added in v0.5.3
Duration is a session-file duration: "90s", "2m", "1h30m". A bare integer is seconds. Zero means unset.
func (Duration) MarshalJSON ¶ added in v0.5.3
MarshalJSON writes the duration in Go's notation ("2m0s").
func (Duration) MarshalYAML ¶ added in v0.5.3
MarshalYAML writes the duration in Go's notation ("2m0s").
func (*Duration) UnmarshalJSON ¶ added in v0.5.3
UnmarshalJSON accepts "2m" or a number of seconds.
type ExpectResponse ¶
type ExpectResponse struct {
Status []int `yaml:"status,omitempty" json:"status,omitempty"` // Acceptable status codes (e.g. [200, 201])
BodyContains string `yaml:"body_contains,omitempty" json:"body_contains,omitempty"` // Response body must contain this string
}
ExpectResponse defines validation rules for a login response.
type ExtractRule ¶
type ExtractRule struct {
Source ExtractSource `yaml:"source" json:"source"`
Name string `yaml:"name,omitempty" json:"name,omitempty"` // Cookie name or header name
Path string `yaml:"path,omitempty" json:"path,omitempty"` // JSONPath for json source
ApplyAs string `yaml:"apply_as,omitempty" json:"apply_as,omitempty"` // Header template, e.g. "Authorization: Bearer {value}"
// Regex extraction fields (source: "regex")
Pattern string `yaml:"pattern,omitempty" json:"pattern,omitempty"` // Regex pattern with capture group
Group int `yaml:"group,omitempty" json:"group,omitempty"` // Capture group index (default: 1)
}
ExtractRule defines how to extract an auth token from a login response.
type ExtractSource ¶
type ExtractSource string
ExtractSource specifies where to extract authentication tokens from a login response.
const ( ExtractCookie ExtractSource = "cookie" // Extract from Set-Cookie header ExtractJSON ExtractSource = "json" // Extract from JSON response body ExtractHeader ExtractSource = "header" // Extract from response header ExtractRegex ExtractSource = "regex" // Extract from response body via regex )
type LintIssue ¶
type LintIssue struct {
Field string `json:"field"` // dot-notation path, e.g. "sessions[0].login.url"
Severity string `json:"severity"` // "error" or "warning"
Message string `json:"message"`
// Line and Column locate the field in the source file (1-based); 0 when
// the issue has no position (a missing field with no present parent, or a
// document parsed without one).
Line int `json:"line,omitempty"`
Column int `json:"column,omitempty"`
}
LintIssue represents a single validation issue found during linting.
func LintSessionConfig ¶
func LintSessionConfig(cfg *SessionConfig) []LintIssue
LintSessionConfig performs detailed validation of a SessionConfig and returns all issues found, rather than stopping at the first error.
type LiveEvent ¶ added in v0.5.3
type LiveEvent struct {
Kind LiveEventKind
Session string
// Gen is the generation the event is about: the suspected one for suspect
// and a failed relogin, the NEW one for a successful relogin.
Gen uint64
OK bool
// Reason says what triggered a suspect: "status", "redirect", "body"
// (a logged_out match), "interval" (the proactive probe), "expiry" (the
// credentials are about to expire) or "invalidate".
Reason string
// Attempts is how many logins a relogin cycle ran.
Attempts int
Err error
OnFailure ReauthOnFailure
}
LiveEvent is what a LiveSession tells its owner. Delivered synchronously from the confirm/refresh goroutine, never from the request path.
type LiveEventKind ¶ added in v0.5.3
type LiveEventKind string
LiveEventKind names what a LiveEvent reports.
const ( // LiveEventSuspect: scan traffic (or the proactive probe) looked logged out. LiveEventSuspect LiveEventKind = "suspect" // LiveEventRelogin: a re-login cycle ended; OK says how. LiveEventRelogin LiveEventKind = "relogin" // LiveEventLost: the session is logged out for good (re-login failed or is // disabled). The scan continues unauthenticated or aborts per on_failure. LiveEventLost LiveEventKind = "lost" )
type LiveOptions ¶ added in v0.5.3
type LiveOptions struct {
// Manager re-runs the login (rehydrate) and supplies the probe transport.
// Nil disables re-login: a confirmed logout goes straight to failed.
Manager *Manager
// ProbeClient sends the check.url probe. Nil uses the manager's login
// transport with redirects not followed (Go's default transport when
// there is no manager).
ProbeClient *http.Client
// ProbeBase resolves a relative check.url (a scan target). Empty falls back
// to the login URL.
ProbeBase string
// Pinned are header names the operator set explicitly (-H): the session
// never overrides them.
Pinned []string
// OnEvent receives suspect / relogin / lost events. Optional.
OnEvent func(LiveEvent)
// Context bounds the probes and re-logins; Close cancels it too.
Context context.Context
// WaitBudget bounds how long Wait parks a request during a re-login.
// Zero uses the manager's login budget (DefaultLoginBudget).
WaitBudget time.Duration
// Test seams. Nil means the real clock.
Now func() time.Time
Sleep func(ctx context.Context, d time.Duration) error
}
LiveOptions configures a LiveSession.
type LiveSession ¶ added in v0.5.3
type LiveSession struct {
// contains filtered or unexported fields
}
LiveSession is the scan-time owner of one session's credentials. All methods are safe for concurrent use; a nil *LiveSession is inert.
func NewLiveSession ¶ added in v0.5.3
func NewLiveSession(sess *Session, opt LiveOptions) (*LiveSession, error)
NewLiveSession builds the live owner of sess, which must be hydrated and carry a check block (call Session.ApplyDefaults first) or credentials with a known expiry (an OAuth2 login). It starts the proactive probe timer when check.interval is set and the expiry timer when the credentials expire; Close stops both.
func (*LiveSession) Close ¶ added in v0.5.3
func (s *LiveSession) Close()
Close stops the proactive probe, cancels any probe or login in flight and waits for them to return. Idempotent.
func (*LiveSession) Credentials ¶ added in v0.5.3
func (s *LiveSession) Credentials() *Credentials
Credentials is the current generation. Lock-free; never nil on a live session (nil on a nil receiver).
func (*LiveSession) Invalidate ¶ added in v0.5.3
func (s *LiveSession) Invalidate(gen uint64) bool
Invalidate reports generation gen as logged out, skipping the confirm probe. Only the first caller for the current generation starts a re-login; it returns whether this call did.
func (*LiveSession) LoggedOutResponses ¶ added in v0.5.3
func (s *LiveSession) LoggedOutResponses() int64
LoggedOutResponses counts responses the scan received while the session was logged out: every logged_out match (from any generation), plus every response once the session has failed. Work behind them ran unauthenticated; v1 reports the count rather than re-queueing it.
func (*LiveSession) Name ¶ added in v0.5.3
func (s *LiveSession) Name() string
Name is the session's name.
func (*LiveSession) Observe ¶ added in v0.5.3
Observe feeds one scan response, sent with generation gen's credentials, to the logged-out detector. It never blocks: status and Location are checked on every response, the logged_out body regex on one in check.sample_every (first 64 KiB), and a match that needs confirming hands off to a goroutine.
func (*LiveSession) Observed ¶ added in v0.5.3
func (s *LiveSession) Observed() int64
Observed counts the responses Observe was given.
func (*LiveSession) OnFailure ¶ added in v0.5.3
func (s *LiveSession) OnFailure() ReauthOnFailure
OnFailure is the session's reauth.on_failure policy.
func (*LiveSession) State ¶ added in v0.5.3
func (s *LiveSession) State() LiveState
State is where the session is in its cycle.
func (*LiveSession) Wait ¶ added in v0.5.3
func (s *LiveSession) Wait(ctx context.Context) error
Wait parks the caller while the session confirms a suspected logout and re-logs in, when reauth pause_traffic is on (the default), so requests are not sent with credentials about to be replaced. An expiry refresh does not park (the credentials are still good while it runs). It returns at once when nothing is held, when the cycle ends, after the login budget, or with ctx's error when ctx ends first. Call it BEFORE taking an egress host slot: a parked request must not hold a slot the re-login needs.
type LiveState ¶ added in v0.5.3
type LiveState int32
LiveState is where a LiveSession is in its cycle.
const ( // LiveValid: the credentials are believed good. LiveValid LiveState = iota // LiveSuspect: scan traffic looked logged out; the confirm probe is running. LiveSuspect // LiveRefreshing: confirmed logged out; the login flow is re-running. LiveRefreshing // LiveFailed: re-login gave up (or is disabled). Terminal for the scan. LiveFailed )
type LoggedInRule ¶ added in v0.5.3
type LoggedInRule struct {
Status []int `yaml:"status,omitempty" json:"status,omitempty"`
BodyContains string `yaml:"body_contains,omitempty" json:"body_contains,omitempty"`
BodyRegex string `yaml:"body_regex,omitempty" json:"body_regex,omitempty"`
// HeaderRegex is matched against each response header rendered as
// "Name: value" (e.g. 'Content-Type: application/json').
HeaderRegex string `yaml:"header_regex,omitempty" json:"header_regex,omitempty"`
}
LoggedInRule holds on a response that proves the session is logged in. ALL configured conditions must hold.
type LoggedOutRule ¶ added in v0.5.3
type LoggedOutRule struct {
Status []int `yaml:"status,omitempty" json:"status,omitempty"`
// RedirectRegex is matched against the Location header of a 3xx.
RedirectRegex string `yaml:"redirect_regex,omitempty" json:"redirect_regex,omitempty"`
BodyRegex string `yaml:"body_regex,omitempty" json:"body_regex,omitempty"`
}
LoggedOutRule marks a response as a sign the session is gone. ANY matching condition counts.
type LoginFlow ¶
type LoginFlow struct {
URL string `yaml:"url" json:"url"`
Method string `yaml:"method" json:"method"`
ContentType string `yaml:"content_type,omitempty" json:"content_type,omitempty"`
Body string `yaml:"body,omitempty" json:"body,omitempty"`
Extract []ExtractRule `yaml:"extract,omitempty" json:"extract,omitempty"`
// Shorthand: type + token_path expand into extract rules automatically.
Type LoginType `yaml:"type,omitempty" json:"type,omitempty"`
TokenPath string `yaml:"token_path,omitempty" json:"token_path,omitempty"`
// Response validation.
Expect *ExpectResponse `yaml:"expect,omitempty" json:"expect,omitempty"`
// Multi-step login flows. When set, URL/Method/Body/Extract on the parent are ignored.
Steps []LoginStep `yaml:"steps,omitempty" json:"steps,omitempty"`
// Headers are extra request headers sent with every login request: the
// single request, each step (a step's own headers win), or the OAuth2
// token request. Values support the {{env:…}} / {{totp:…}} templates.
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
// OAuth2 token endpoint (type oauth2_client_credentials or
// oauth2_refresh_token; see oauth.go). Every value supports the
// {{env:…}} templates; ${ENV} is expanded when the file loads.
TokenURL string `yaml:"token_url,omitempty" json:"token_url,omitempty"`
ClientID string `yaml:"client_id,omitempty" json:"client_id,omitempty"`
ClientSecret string `yaml:"client_secret,omitempty" json:"client_secret,omitempty"`
// Scope is space- (or comma-) separated. Sent with client_credentials.
Scope string `yaml:"scope,omitempty" json:"scope,omitempty"`
// Audience is sent as the `audience` parameter with client_credentials.
Audience string `yaml:"audience,omitempty" json:"audience,omitempty"`
RefreshToken string `yaml:"refresh_token,omitempty" json:"refresh_token,omitempty"`
// AuthStyle is how the client authenticates: "header" (HTTP Basic) or
// "body" (form fields). Empty tries header, then body.
AuthStyle string `yaml:"auth_style,omitempty" json:"auth_style,omitempty"`
// contains filtered or unexported fields
}
LoginFlow defines how to authenticate to get session credentials.
type LoginRequest ¶
type LoginRequest struct {
Method string
URL string
Headers map[string]string
Body string
Raw string // original raw request text
}
LoginRequest holds a parsed raw HTTP login request.
func ParseRawLoginRequest ¶
func ParseRawLoginRequest(raw string) (*LoginRequest, error)
ParseRawLoginRequest parses a raw HTTP request string into a LoginRequest. Supports the format:
POST /rest/user/login HTTP/1.1
Host: localhost:3000
Content-Type: application/json
{"email":"admin@juice-sh.op","password":"admin123"}
type LoginResult ¶ added in v0.5.3
type LoginResult struct {
Session string `json:"session"`
// OK is false when the login failed: exit 1 for `auth load`. An OK result
// with a Reason (unverified) is a warning.
OK bool `json:"ok"`
// Verified: an expect block passed, or the response showed the login
// worked (a credential extracted, or a redirect away from the login URL)
// with no login form left in it.
Verified bool `json:"verified"`
// Status is the status of the last response the flow received.
Status int `json:"status,omitempty"`
// URL is the request URL of the last request sent (the failing step's, for
// a multi-step flow that stopped early).
URL string `json:"url,omitempty"`
// FinalURL is where that request ended up after redirects.
FinalURL string `json:"final_url,omitempty"`
Redirects []string `json:"redirects,omitempty"`
// Step is the index of the last step attempted in a multi-step flow; absent
// for a single-request flow.
Step *int `json:"step,omitempty"`
// ExtractedHeaders names (never values) the headers the login produced.
ExtractedHeaders []string `json:"extracted_headers,omitempty"`
// ExpiresAt is when the credentials expire, when the login said so (an
// OAuth2 token's expires_in).
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Reason string `json:"reason,omitempty"`
// Message explains a failure or a warning in prose.
Message string `json:"message,omitempty"`
// Err is the failure, for Go callers.
Err error `json:"-"`
}
LoginResult is what CheckLogin observed for one session.
func CheckLogin ¶ added in v0.5.3
CheckLogin runs sess's login flow — single request or multi-step `steps`, with every `expect` block enforced — and reports what happened instead of a bare error. It hydrates sess on success, exactly as a scan's login does, because it IS a scan's login (executeLogin), not a separate probe.
client supplies the transport (proxy, target TLS; see pkg/http.LoginTransport) and is copied, never mutated. A nil client uses Go's default transport.
A session with only login_request is converted first, as NewManager does.
With no expect block, success is unproven, so the response is judged: a password input still in the final body means the login form came back (unverified, with that said); a credential extracted or a redirect away from the login URL verifies it; anything else is unverified. Unverified is OK with a warning, never a failure: only an explicit expect, a status outside 2xx/3xx, a failed extract or a transport error fails.
type LoginStep ¶
type LoginStep struct {
URL string `yaml:"url" json:"url"`
Method string `yaml:"method" json:"method"`
ContentType string `yaml:"content_type,omitempty" json:"content_type,omitempty"`
// Headers are extra request headers for this step, applied after the
// flow's own login.headers (a step header wins). Values support {var}
// and the {{env:…}} / {{totp:…}} templates, like url and body.
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
Body string `yaml:"body,omitempty" json:"body,omitempty"`
Extract []ExtractRule `yaml:"extract,omitempty" json:"extract,omitempty"`
Expect *ExpectResponse `yaml:"expect,omitempty" json:"expect,omitempty"`
}
LoginStep defines a single step in a multi-step login flow.
type LoginType ¶
type LoginType string
LoginType is a shorthand preset that auto-expands into extract rules.
const ( LoginTypeBearer LoginType = "bearer" // JSON body → Authorization: Bearer {value} LoginTypeCookie LoginType = "cookie" // Capture all Set-Cookie headers // LoginTypeOAuth2ClientCredentials fetches an access token from token_url // with the client_credentials grant (oauth.go). LoginTypeOAuth2ClientCredentials LoginType = "oauth2_client_credentials" // LoginTypeOAuth2RefreshToken trades refresh_token for an access token at // token_url (oauth.go). LoginTypeOAuth2RefreshToken LoginType = "oauth2_refresh_token" )
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager loads, validates, and hydrates sessions for multi-session scanning.
func NewManager ¶
func NewManager(sessions []*Session, opts ...ManagerOption) (*Manager, error)
NewManager creates a Manager from the resolved session list.
func (*Manager) AllSessions ¶
AllSessions returns all sessions.
func (*Manager) CompareSessions ¶
CompareSessions returns all non-primary sessions used for comparison.
func (*Manager) HydrateSessions ¶
HydrateSessions executes login flows for sessions that need them on a background context. It is the convenience wrapper for callers with no context of their own; anything inside a scan should call HydrateSessionsContext so a cancelled scan stops logging in.
func (*Manager) HydrateSessionsContext ¶ added in v0.5.2
HydrateSessionsContext executes login flows for sessions that need them, bounded by ctx AND by the manager's total login budget (whichever expires first). The budget wraps the WHOLE loop rather than each session, so N sessions cannot cost N × the budget; a flow that runs out mid-way reports which session it died on.
A cancelled or expired ctx is reported before the first request, so a cancelled scan does not start a login it is about to abandon.
func (*Manager) LoginBudget ¶ added in v0.5.3
LoginBudget is the total time one hydration (or one re-login) may take.
func (*Manager) PrimaryHeaders ¶
PrimaryHeaders returns the primary session's headers as a slice for types.Options.Headers.
type ManagerOption ¶
type ManagerOption func(*Manager)
ManagerOption configures optional Manager behavior.
func WithLoginBudget ¶ added in v0.5.2
func WithLoginBudget(d time.Duration) ManagerOption
WithLoginBudget overrides the total time all login flows together may take. A non-positive value keeps DefaultLoginBudget.
func WithLoginTransport ¶ added in v0.5.2
func WithLoginTransport(rt http.RoundTripper) ManagerOption
WithLoginTransport routes login requests over rt. A login request targets the host the scan is about to attack, so it belongs on the same proxy and the same permissive TLS stance as the rest of the scan's traffic; pkg/http.LoginTransport builds exactly that. A nil rt is ignored (Go's default transport stands).
func WithSessionDir ¶
func WithSessionDir(dir string) ManagerOption
WithSessionDir overrides the default directory used to resolve session file names.
type ParseError ¶ added in v0.5.3
ParseError is a session file that could not be decoded at all. Line is 0 when the decoder did not say where.
func (*ParseError) Error ¶ added in v0.5.3
func (e *ParseError) Error() string
func (*ParseError) Unwrap ¶ added in v0.5.3
func (e *ParseError) Unwrap() error
type ParseOptions ¶ added in v0.5.3
type ParseOptions struct {
// Strict reports unknown fields as errors (auth lint). Otherwise they are
// warnings, so an existing file with an extra key keeps loading.
Strict bool
// Agent forces the lenient agent session-config.json decoder (auth load
// --agent-format, or a path that names an agent session file).
Agent bool
// KeepEnvRefs leaves ${ENV} references unexpanded, for a caller that
// writes the sessions back to a file another loader expands (expanding
// twice would mangle a value containing '$').
KeepEnvRefs bool
}
ParseOptions tunes ParseSessionDocument.
type ProbeResponse ¶ added in v0.5.3
type ProbeResponse struct {
Status int
Header http.Header
Body []byte
// URL is the request URL, used to resolve a relative Location.
URL string
Err error
}
ProbeResponse is one response the logged-in rules judge. Err set means the request did not complete.
func (*ProbeResponse) Location ¶ added in v0.5.3
func (p *ProbeResponse) Location() string
Location is the response's Location header, resolved against URL.
type ReauthOnFailure ¶ added in v0.5.3
type ReauthOnFailure string
ReauthOnFailure is what a scan does when re-login keeps failing.
const ( // ReauthContinue keeps scanning unauthenticated; the phase is partial with // reason auth_lost. The default (decision D19). ReauthContinue ReauthOnFailure = "continue" // ReauthAbort cancels the scan with a typed error. ReauthAbort ReauthOnFailure = "abort" )
type Session ¶
type Session struct {
Name string `yaml:"name" json:"name"`
Role Role `yaml:"role" json:"role"`
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
Login *LoginFlow `yaml:"login,omitempty" json:"login,omitempty"`
// LoginRequest is a raw HTTP request string for the login flow.
LoginRequest string `yaml:"login_request,omitempty" json:"login_request,omitempty"`
// Check says how to tell whether the session is still logged in
// (loggedin.go). Optional; scanning_strategy.session supplies defaults.
Check *SessionCheck `yaml:"check,omitempty" json:"check,omitempty"`
// Reauth says what a scan does once Check finds the session logged out.
Reauth *SessionReauth `yaml:"reauth,omitempty" json:"reauth,omitempty"`
// contains filtered or unexported fields
}
Session represents a named authentication identity used during scanning.
func LoadFromAuthFiles ¶
LoadFromAuthFiles loads sessions from one or more --auth-file values. Each value is either a file path (YAML/JSON: a sessions: bundle, a single session, or an agent session-config.json) or a bare name resolved against sessionDir (default ~/.vigolium/sessions/), trying .yaml, .yml, .json in order. Files support ${ENV} expansion before parsing.
An unknown field is logged as a warning, not refused: a file that loaded before the parser learned to notice extra keys keeps loading. `auth lint` reports the same field as an error, with its line.
func LoadFromAuthInline ¶
LoadFromAuthInline parses --auth values in "name:Header:value" format.
func ParseInlineSession ¶
ParseInlineSession parses a CLI --auth flag value in "name:Header:value" format. Example: "admin:Cookie:session=abc" → Session{Name:"admin", Headers:{"Cookie":"session=abc"}}
func (*Session) ApplyDefaults ¶ added in v0.5.3
func (s *Session) ApplyDefaults(d SessionDefaults)
ApplyDefaults fills s's check block from d: a value the session sets always wins. A session with no check block gets one when d has anything to say. The session's own block is copied, never mutated in place, so a Session shared between callers is not changed behind their back.
func (*Session) ExpiresAt ¶ added in v0.5.3
ExpiresAt is when the session's current credentials expire, as reported by the login that produced them (an OAuth2 token response's expires_in). Zero when the login did not say. A scan's LiveSession refreshes the credentials shortly before this time.
func (*Session) HeaderSlice ¶
HeaderSlice converts the session headers map to a slice of "Name: Value" strings, compatible with types.Options.Headers.
func (*Session) IsHydrated ¶
IsHydrated returns true if the session has been populated with credentials.
func (*Session) ReauthEnabled ¶ added in v0.5.3
ReauthEnabled reports whether a scan should re-login s when its check says it is logged out, or ahead of its credentials' known expiry: it needs a check to notice (or an expiry to refresh ahead of) and a login flow to re-run, and `reauth.enabled: false` turns it off.
type SessionCheck ¶ added in v0.5.3
type SessionCheck struct {
// URL is the active probe. A path ("/api/me") resolves against the target
// origin. Optional when logged_out has a status or redirect indicator.
URL string `yaml:"url,omitempty" json:"url,omitempty"`
Method string `yaml:"method,omitempty" json:"method,omitempty"`
// LoggedIn must hold on the probe's response.
LoggedIn *LoggedInRule `yaml:"logged_in,omitempty" json:"logged_in,omitempty"`
// LoggedOut flags scan traffic (and the probe) as logged out.
LoggedOut *LoggedOutRule `yaml:"logged_out,omitempty" json:"logged_out,omitempty"`
// SampleEvery evaluates the logged_out body regex on 1 in N scan
// responses (status and Location are checked on every response). 0 means
// DefaultCheckSampleEvery.
SampleEvery int `yaml:"sample_every,omitempty" json:"sample_every,omitempty"`
// Interval runs the active probe proactively at most this often; 0 probes
// only when traffic looks logged out.
Interval Duration `yaml:"interval,omitempty" json:"interval,omitempty"`
}
SessionCheck is a session's `check` block: how to tell whether it is still logged in.
func (*SessionCheck) Compile ¶ added in v0.5.3
func (c *SessionCheck) Compile() (*CheckMatcher, error)
Compile compiles the check's regexes. A nil check compiles to nil.
func (*SessionCheck) EffectiveMethod ¶ added in v0.5.3
func (c *SessionCheck) EffectiveMethod() string
EffectiveMethod is Method or GET.
func (*SessionCheck) EffectiveSampleEvery ¶ added in v0.5.3
func (c *SessionCheck) EffectiveSampleEvery() int
EffectiveSampleEvery is SampleEvery or DefaultCheckSampleEvery.
type SessionConfig ¶
type SessionConfig struct {
Sessions []Session `yaml:"sessions" json:"sessions"`
}
SessionConfig holds multiple sessions, used for --auth-config files (YAML or JSON).
type SessionDefaults ¶ added in v0.5.3
type SessionDefaults struct {
// ValidateURL fills check.url.
ValidateURL string
// ReauthOnStatus fills check.logged_out.status.
ReauthOnStatus []int
// ReauthInterval fills check.interval.
ReauthInterval time.Duration
}
SessionDefaults are the config-wide values (scanning_strategy.session) that fill in what a session's own check block leaves unset.
type SessionDocument ¶ added in v0.5.3
type SessionDocument struct {
Form SessionForm
Sessions []*Session
// Issues are what parsing itself found: unknown fields, with line/column.
// Semantic problems come from Lint.
Issues []LintIssue
// contains filtered or unexported fields
}
SessionDocument is a parsed session file.
func ParseSessionDocument ¶ added in v0.5.3
func ParseSessionDocument(content []byte, name string, opt ParseOptions) (*SessionDocument, error)
ParseSessionDocument is ParseSessionBytes without the "at least one session" rule, and with the form and source positions kept for Lint.
func (*SessionDocument) Lint ¶ added in v0.5.3
func (d *SessionDocument) Lint() []LintIssue
Lint runs the semantic checks over the document's sessions (field paths follow its form: sessions[0].login.url for a bundle, login.url for a single session) and attaches source positions where the field, or its nearest present parent, appears in the file. Parse-level Issues are not included.
type SessionForm ¶ added in v0.5.3
type SessionForm string
SessionForm names which accepted document shape a session file used.
const ( // FormBundle is `sessions: [...]`. FormBundle SessionForm = "bundle" // FormSingle is one session at the top level (name, role, login, ...). FormSingle SessionForm = "single" // FormAgent is the agent's session-config.json: a bundle written by a model, // decoded leniently (status as strings, group as a string). FormAgent SessionForm = "agent" )
type SessionReauth ¶ added in v0.5.3
type SessionReauth struct {
// Enabled defaults to true when the session has a check block.
Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
// MaxAttempts is the number of consecutive failed re-logins before giving
// up. 0 means DefaultReauthMaxAttempts.
MaxAttempts int `yaml:"max_attempts,omitempty" json:"max_attempts,omitempty"`
// Backoff is the wait after the first failure; it doubles per failure.
// 0 means DefaultReauthBackoff.
Backoff Duration `yaml:"backoff,omitempty" json:"backoff,omitempty"`
OnFailure ReauthOnFailure `yaml:"on_failure,omitempty" json:"on_failure,omitempty"`
// PauseTraffic holds this session's requests while it re-logs in. Default
// true.
PauseTraffic *bool `yaml:"pause_traffic,omitempty" json:"pause_traffic,omitempty"`
}
SessionReauth is a session's `reauth` block: what to do once the check confirms it is logged out.
func (*SessionReauth) EffectiveBackoff ¶ added in v0.5.3
func (r *SessionReauth) EffectiveBackoff() time.Duration
EffectiveBackoff is Backoff or DefaultReauthBackoff.
func (*SessionReauth) EffectiveMaxAttempts ¶ added in v0.5.3
func (r *SessionReauth) EffectiveMaxAttempts() int
EffectiveMaxAttempts is MaxAttempts or DefaultReauthMaxAttempts.
func (*SessionReauth) EffectiveOnFailure ¶ added in v0.5.3
func (r *SessionReauth) EffectiveOnFailure() ReauthOnFailure
EffectiveOnFailure is OnFailure or ReauthContinue.
func (*SessionReauth) EffectivePauseTraffic ¶ added in v0.5.3
func (r *SessionReauth) EffectivePauseTraffic() bool
EffectivePauseTraffic is PauseTraffic or true.
type Signal ¶ added in v0.5.3
type Signal struct {
Name string `json:"name"`
Side string `json:"side,omitempty"`
Detail string `json:"detail,omitempty"`
}
Signal is one piece of evidence AssessAuthenticated used. Side is "auth" or "anon" when the signal is about one response, empty for a comparison.
type Verdict ¶ added in v0.5.3
type Verdict string
Verdict is what AssessAuthenticated concluded about a session on one URL.
const ( // VerdictAuthenticated: the session reached content the anonymous baseline // did not (or the configured logged_in rule held and the baseline failed // it). VerdictAuthenticated Verdict = "authenticated" // VerdictLoginWall: the authenticated request was sent to a login page // (a redirect to a login URL, or a password form in the body). VerdictLoginWall Verdict = "login_wall" // configured logged_out status, or a failed logged_in rule). VerdictUnauthorized Verdict = "unauthorized" // VerdictIndistinguishable: the authenticated and anonymous responses are // the same, so the URL proves nothing (a public page, or credentials the // server ignores). Pick a protected URL or configure check.logged_in. VerdictIndistinguishable Verdict = "indistinguishable" // VerdictError: a request failed or the check block is invalid. VerdictError Verdict = "error" )