Documentation
¶
Overview ¶
Package module defines Geiger's core domain types and the Module contract.
A Module is the unit of credential coverage: it recognizes a credential (via the recognize package routing to it), optionally performs a single headless token exchange, runs read-only recon, and summarizes the result into a Note. The common bearer/basic case is built declaratively via the recipe subpackage; exotic-signing providers implement Module directly.
Index ¶
- Constants
- Variables
- func MapRule(ruleID, moduleName string)
- func Register(m Module)
- func ValidateEndpointURL(s string) error
- type Base
- type Candidate
- type EndpointPolicy
- type EndpointScoped
- type Fields
- type Finding
- type FlagLevel
- type Harvested
- type Harvester
- type Module
- type Note
- type Registry
- func (r *Registry) All() []Module
- func (r *Registry) ByName(name string) (Module, bool)
- func (r *Registry) ByRule(ruleID string) (Module, bool)
- func (r *Registry) MapRule(ruleID, moduleName string)
- func (r *Registry) Register(m Module)
- func (r *Registry) RuleModule(ruleID string) (string, bool)
- func (r *Registry) Rules() map[string]string
- type SourceKind
- type Token
Constants ¶
const ExposureKey = "exposure"
ExposureKey is the finding key recording WHERE a credential was exposed. Named so the renderer can single it out on a dead note, whose other detail is suppressed.
Variables ¶
var Default = NewRegistry()
Default is the process-wide registry that modules self-register into.
var URLValuedFields = []string{"endpoint", "host", "api", "server"}
URLValuedFields are the field names whose value is a whole base URL, as opposed to a hostname label spliced into one. These are what EndpointPolicy polices.
Functions ¶
func MapRule ¶
func MapRule(ruleID, moduleName string)
MapRule routes a rule id in the default registry.
func ValidateEndpointURL ¶ added in v1.8.0
ValidateEndpointURL enforces the structural rules on a URL that will carry a credential: absolute http(s), a host present, and no embedded userinfo.
Types ¶
type Base ¶
type Base struct{}
Base provides a no-op Authenticate so direct-auth modules can embed it.
type Candidate ¶
type Candidate struct {
Value string // the raw credential string (a token, key, JSON, etc.)
Source SourceKind // how it was parsed
File string // origin filename/label, if any
Vars map[string]string // co-located key/value pairs (env, dotenv, INI section)
}
Candidate is a single recognized-or-candidate credential plus the context it was found in. Source context lets set-shaped recognizers pair co-located variables and pick up a tenant/instance/host from the same blob.
type EndpointPolicy ¶ added in v1.8.0
type EndpointPolicy struct {
// SelfHosted permits any host. Correct for services deployable at an
// arbitrary domain — Vault, Splunk, GitLab, and every vendor shipping both a
// SaaS and an on-prem edition. Pinning suffixes for those would break real
// customer deployments, which is why geiger has no global host allowlist.
SelfHosted bool
// HostSuffixes pins a SaaS-only vendor to its own domains. A data-derived
// host must equal one of these or be a subdomain of it. Include every region
// and government host the vendor operates: a missing suffix is a broken
// deployment, not a safe default.
HostSuffixes []string
}
EndpointPolicy declares where a module's credential may legitimately be sent.
Endpoints reach geiger from scanned data — a co-located env var, a URL matched out of a raw blob, a host concatenated by a recognizer — so they are untrusted: a planted value redirects a real credential to whoever planted it. Every module templated on a URL-valued field ({endpoint}, {host}, {api}, {server}) declares one of these, and recognize.Recognize enforces it centrally, before any module code runs. That placement is deliberate: a module that resolves its own host in a hand-written recognizer or an Authenticate hook cannot route around it.
This polices the URL as a whole. It is NOT a substitute for recipe.renderBase, which polices field values spliced into a *segment* of a base template ("https://{shop}.myshopify.com") — that check needs the rendered template and so can only happen later. Both are required; neither subsumes the other.
func (EndpointPolicy) Declared ¶ added in v1.8.0
func (p EndpointPolicy) Declared() bool
Declared reports whether a policy was actually stated, as opposed to being the zero value. recipe.HTTP embeds an EndpointPolicy by value, so every recipe module satisfies EndpointScoped whether or not its author filled the field in; the catalog guard asserts on this rather than on the interface, or it would pass vacuously.
func (EndpointPolicy) HostAllowed ¶ added in v1.8.0
func (p EndpointPolicy) HostAllowed(host string) bool
HostAllowed reports whether host satisfies the policy. Matching is on a label boundary, so "zendesk.com" accepts "acme.zendesk.com" but not "evil-zendesk.com" or "zendesk.com.attacker.tld".
type EndpointScoped ¶ added in v1.8.0
type EndpointScoped interface {
EndpointPolicy() EndpointPolicy
}
EndpointScoped is implemented by modules that declare an EndpointPolicy. Modules without one get no host restriction (their endpoint is still required to be a structurally valid http(s) URL).
type Fields ¶
Fields are the recognizer's extracted, named inputs to a module (access key, secret, tenant, instance URL, …). Endpoint-bearing fields may be filled from the blob, a default, or the --endpoint flag.
type Finding ¶
type Finding struct {
Key string // short stable key (identity, account, scopes, buckets, …)
Value string // human-readable value (already redacted where needed)
Flag FlagLevel
// Detail holds the full expansion behind a summarized Value (e.g. the
// individual file paths behind "8 editor local-history snapshots"). The
// terminal shows it only with -v; JSON always emits it. Optional.
Detail []string
}
Finding is one line of a Note: a labeled value with a significance flag.
type FlagLevel ¶
type FlagLevel int
FlagLevel classifies the significance of a finding for the note.
type Harvested ¶
type Harvested struct {
Label string // provenance, e.g. "secretsmanager:prod/db-password"
Value string // the extracted secret value
}
Harvested is a downstream secret pulled from a secrets store, to be fed back through recognition and triaged recursively.
type Harvester ¶
type Harvester interface {
Harvest(ctx context.Context, c *recon.Client, t Token, f Fields) ([]Harvested, error)
}
Harvester is implemented by modules that can read a secrets store. Harvest EXTRACTS secret values (not just metadata), so the pipeline only calls it under --live --intrusive and within a bounded recursion depth/budget.
type Module ¶
type Module interface {
// Name is the stable module identifier (also used for dedupe).
Name() string
// Authenticate performs the optional single headless token exchange.
// Modules that need no exchange return an empty Token and nil error.
Authenticate(ctx context.Context, c *recon.Client, f Fields) (Token, error)
// Recon runs the read-only recipe and returns findings.
Recon(ctx context.Context, c *recon.Client, t Token, f Fields) ([]Finding, error)
// Summarize turns findings into the printed Note.
Summarize(title string, fs []Finding) Note
}
Module is the unit of credential coverage.
type Note ¶
type Note struct {
Title string // e.g. "GitHub PAT ghp_…JV3Q (from .env: GITHUB_TOKEN)"
Findings []Finding // ordered lines
Summary string // one-line takeaway, e.g. "org-admin bot token"
Invalid bool // recon proved the credential dead/expired
Reason string // why invalid, or why it could not be characterized
// Provenance, also carried in the Title for humans. Machine formats need it
// structurally: SARIF keys a rule off Module and a location off File/Line.
Module string // module that characterized the credential, e.g. "aws"
File string // source the credential came from (path, URL, or "environment")
Line int // 1-based line within File, 0 when unknown
// Fingerprint is an upstream scanner's id for the finding this came from,
// passed through verbatim so their viewer can dedupe against their own.
Fingerprint string
// Undetermined marks a credential geiger tried to characterize and could
// not — the host was unreachable, the format has no module to exercise, a
// passphrase is missing. It is NOT dead (nothing disproved it) and it has no
// demonstrated reach, so scoring it from descriptive findings alone would
// invent a severity. Set it and the tier reads UNKNOWN, with Reason saying
// why.
Undetermined bool
}
Note is a module's summary for one credential.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds the available modules and the routes into them.
func (*Registry) Register ¶
Register adds a module. It panics on a duplicate name (a programming error).
func (*Registry) RuleModule ¶
RuleModule returns the module name a rule maps to.
type SourceKind ¶
type SourceKind string
SourceKind records how a candidate was produced.
const ( SourceStdin SourceKind = "stdin" SourceFile SourceKind = "file" SourceEnv SourceKind = "env" SourceDotenv SourceKind = "dotenv" SourceINI SourceKind = "ini" SourceJSON SourceKind = "json" SourceKube SourceKind = "kubeconfig" SourceRegistry SourceKind = "registry" )