Documentation
¶
Overview ¶
internal/secrets/confidence.go
Package secrets finds credentials embedded in container images, filesystems, and Dockerfiles. It is a from-scratch implementation of provider-aware secret detection: a data-driven ruleset of provider-specific detectors, an entropy fallback, layer-aware image scanning (including content deleted by a later whiteout, which stays extractable and is a classic leak), a versioned baseline/allowlist, and an opt-in live-verification hook.
Two principles shape the design:
- Precision is a feature. A noisy secret scanner gets muted, and a muted scanner misses the real leak. The default rule set is high-signal; the broad entropy sweep is gated behind an optional classifier so it never floods the default run.
- Values never leave the process. A Detection carries a fingerprint (a truncated SHA-256), a type, a length, and a location — never the secret itself. Even the opt-in verifier receives the raw value transiently and the scanner discards it immediately.
The package is deterministic: given the same bytes it emits the same Detections in the same order, with no reliance on the wall clock or a random source. Verification (the one network-touching feature) is strictly opt-in and injected, so tests never reach the network.
Index ¶
- func SortDetections(ds []Detection)
- func SupportedVerifiers() []string
- type Baseline
- type BaselineEntry
- type Candidate
- type Classifier
- type Detection
- type HTTPVerifier
- type HeuristicClassifier
- type Honeytoken
- type Kind
- type Option
- type Rule
- type Scanner
- func (s *Scanner) ScanDockerfile(ctx context.Context, path string, content []byte) []Detection
- func (s *Scanner) ScanImage(ctx context.Context, img *oci.Image) []Detection
- func (s *Scanner) ScanImageDetailed(ctx context.Context, img *oci.Image) ([]Detection, []string)
- func (s *Scanner) ScanText(ctx context.Context, path string, data []byte, src Source) []Detection
- func (s *Scanner) ScanTree(ctx context.Context, tree *oci.FileTree) []Detection
- type Source
- type Verdict
- type Verifier
- type VerifierFunc
- type VerifyState
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func SortDetections ¶
func SortDetections(ds []Detection)
SortDetections orders detections deterministically so identical input yields byte-identical output. The ordering is stable across runs and machines: non-deleted before deleted, then by layer, path, line, rule code, and finally fingerprint to break any remaining ties.
func SupportedVerifiers ¶
func SupportedVerifiers() []string
SupportedVerifiers returns the detector slugs HTTPVerifier can verify, for diagnostics and docs.
Types ¶
type Baseline ¶
type Baseline struct {
Version int `json:"version"`
Entries []BaselineEntry `json:"entries"`
// contains filtered or unexported fields
}
Baseline is a versioned set of accepted findings.
func LoadBaseline ¶
LoadBaseline reads and validates a baseline JSON file.
func ParseBaseline ¶
ParseBaseline parses baseline JSON from memory. It is separate from LoadBaseline so callers (and tests) can supply bytes directly.
type BaselineEntry ¶
type BaselineEntry struct {
RuleID string `json:"rule_id"`
Fingerprint string `json:"fingerprint"`
Path string `json:"path,omitempty"`
Justification string `json:"justification"`
}
BaselineEntry is a single accepted finding. RuleID and Fingerprint identify it exactly; Path, when present, further scopes the acceptance to one location. Justification is required in spirit (a baseline without reasons rots) and surfaced so reviewers can audit it.
type Classifier ¶
Classifier decides whether a high-entropy Candidate is plausibly a secret. Implementations must be deterministic and side-effect free.
type Detection ¶
type Detection struct {
Code string // stable engine RuleID, e.g. "DS-RAT-SEC-001"
Slug string // machine-readable detector id, e.g. "aws-access-key-id"
Kind Kind // coarse category
Severity engine.Severity // base severity (verification may raise it)
Fingerprint string // truncated SHA-256 of the secret value (never the value)
Entropy float64 // Shannon entropy of the secret value
Confidence string // "high" | "medium" | "low" — corroboration grade (see confidence.go)
Length int // length of the secret value in bytes
Path string // where it was found (file path, or a pseudo-path)
Line int // 1-based line within the source, 0 if not line-oriented
Source Source // what kind of location Path refers to
Deleted bool // found only in a layer removed by a later whiteout
LayerIndex int // image layer index; -1 for the effective filesystem / non-layer sources
LayerDigest string // image layer digest, when known
Verify VerifyState // live-verification result
Title string // human summary, safe to display
Remediation string // structured, agent-consumable fix guidance
References []string // CIS/NIST/vendor references
// contains filtered or unexported fields
}
Detection is a single secret found by the scanner. It is deliberately value-free: Fingerprint identifies the secret without revealing it, so a Detection can be logged, serialized, and diffed safely.
type HTTPVerifier ¶
type HTTPVerifier struct {
// Client is the HTTP client to use. If nil, a client with a short timeout is
// created per call so a hung provider cannot stall a scan.
Client *http.Client
// Endpoints overrides a provider slug's probe URL, for tests. Empty uses the
// real provider URLs.
Endpoints map[string]string
}
HTTPVerifier verifies a secret against its provider with the minimal, side-effect-free authenticated request that provider supports (a read like "who am I", never a write). It is only ever constructed when the operator explicitly opts into verification; providers with no safe probe return VerifyUnknown rather than guessing.
Each provider's probe is a probe struct so the set is data-driven and every endpoint is overridable in tests (Endpoints), letting the whole detect→verify loop run against an httptest server with no real network.
func (HTTPVerifier) Verify ¶
func (v HTTPVerifier) Verify(ctx context.Context, ruleSlug, secret string) VerifyState
Verify implements Verifier for the providers HTTPVerifier understands.
type HeuristicClassifier ¶
type HeuristicClassifier struct{}
HeuristicClassifier is the built-in, model-free Classifier. It rejects the three big sources of entropy-detector noise — UUIDs, fixed-length hex digests (git SHAs, MD5/SHA hashes), and very long base64 asset blobs — and accepts mixed-alphabet tokens that look generated. It is intentionally conservative: when unsure it declines, because a false "secret" erodes trust faster than a missed context-free token (which the provider rules and assignment detector still have a shot at).
func (HeuristicClassifier) Classify ¶
func (HeuristicClassifier) Classify(c Candidate) Verdict
Classify implements Classifier.
type Honeytoken ¶
Honeytoken is a planted decoy credential. Value is what you embed; Fingerprint is how the scanner recognizes it (matching the fingerprint scheme used for real detections, so the two share one code path).
func GenerateHoneytoken ¶
func GenerateHoneytoken(label string) Honeytoken
GenerateHoneytoken derives a stable, AWS-access-key-shaped decoy from label. It performs no I/O and reads no clock or randomness, so it is safe to call in deterministic contexts and reproducible across machines.
type Kind ¶
type Kind string
Kind is a coarse category for a detected secret, used for grouping and for routing verification. It is intentionally small and stable.
const ( KindCloud Kind = "cloud" // cloud provider keys (AWS, GCP, Azure) KindVCS Kind = "vcs" // source-forge tokens (GitHub, GitLab) KindPrivateKey Kind = "private-key" // PEM private-key blocks KindJWT Kind = "jwt" // JSON Web Tokens KindDatabase Kind = "database" // connection strings with credentials KindPayment Kind = "payment" // payment-processor keys (Stripe, ...) KindMessaging Kind = "messaging" // Slack, SendGrid, ... KindGeneric Kind = "generic" // keyword/entropy heuristics KindCanary Kind = "canary" // a planted honeytoken (not a real leak) )
type Option ¶
type Option func(*Scanner)
Option configures a Scanner.
func WithBaseline ¶
WithBaseline suppresses detections the baseline has accepted, while still reporting anything new.
func WithClassifier ¶
func WithClassifier(c Classifier) Option
WithClassifier enables the context-free entropy sweep, routing each candidate through c. Off by default; supplying HeuristicClassifier{} is the intended low-cost, offline enablement.
func WithHoneytokens ¶
func WithHoneytokens(hs ...Honeytoken) Option
WithHoneytokens marks the given canary fingerprints so their appearance is reported as an informational canary rather than a real leak.
func WithMaxFileBytes ¶
WithMaxFileBytes overrides the per-file scan cap (bytes).
func WithVerifier ¶
WithVerifier enables opt-in live verification of detected secrets. The verifier receives raw values and must never log them; the scanner discards them after the call. Never wire a network verifier in tests.
type Rule ¶
type Rule struct {
Code string // stable engine RuleID, e.g. "DS-RAT-SEC-001"
Slug string // machine id, e.g. "aws-access-key-id"
Title string // human summary
Kind Kind // category
Severity engine.Severity // base severity
// contains filtered or unexported fields
}
Rule is one provider-specific detector.
type Scanner ¶
type Scanner struct {
// contains filtered or unexported fields
}
Scanner runs the detector set over content. It is safe to reuse across targets and is free of hidden state: two Scanners built with the same options produce identical results. The optional Classifier, Verifier, Baseline, and honeytoken set are all injected, keeping the default scan deterministic and offline.
func New ¶
New returns a Scanner with the high-signal defaults: provider rules and the keyword-gated assignment detector on; entropy sweep, verification, baseline, and honeytokens all off.
func (*Scanner) ScanDockerfile ¶
ScanDockerfile scans Dockerfile text. Beyond the shared detectors (which catch a token pasted into a RUN or ENV value), this is where ARG/ENV/RUN secrets that never reach a layer file still live, so it is a first-class target rather than an afterthought. The Dockerfile module's DS-RAT-DF-006 flags suspicious *key names*; this complements it by fingerprinting the actual *values* with the full provider ruleset.
func (*Scanner) ScanImage ¶
ScanImage scans a loaded image and returns sorted detections. Any non-fatal problem encountered along the way (currently: an unparsable image config, see scanConfig) is dropped silently here for backward compatibility; use ScanImageDetailed to also receive those as warnings.
func (*Scanner) ScanImageDetailed ¶
ScanImageDetailed is ScanImage plus a warnings slice for non-fatal problems (e.g. an image config that failed to parse — pass 3 then silently contributes nothing, which used to be invisible; see scanConfig). A nil/ empty warnings slice means nothing degraded.
type Verdict ¶
type Verdict struct {
IsSecret bool
Confidence float64 // 0..1
Label string // human-readable reason, e.g. "uuid", "hex-digest", "credential"
}
Verdict is a Classifier's judgment of a Candidate.
type Verifier ¶
type Verifier interface {
Verify(ctx context.Context, ruleSlug, secret string) VerifyState
}
Verifier confirms whether a secret is live. ruleSlug identifies the detector (e.g. "github-token") so a verifier can route to the right provider check.
type VerifierFunc ¶
type VerifierFunc func(ctx context.Context, ruleSlug, secret string) VerifyState
VerifierFunc adapts a function to the Verifier interface, convenient for tests and simple cases.
func (VerifierFunc) Verify ¶
func (f VerifierFunc) Verify(ctx context.Context, ruleSlug, secret string) VerifyState
Verify implements Verifier.
type VerifyState ¶
type VerifyState string
VerifyState records whether a detected secret was checked against its live provider. Verification is opt-in; the default is VerifySkipped.
const ( VerifySkipped VerifyState = "skipped" // verification not attempted (default) VerifyUnknown VerifyState = "unknown" // attempted, provider gave no clear answer VerifyActive VerifyState = "active" // confirmed live — prioritize VerifyInactive VerifyState = "inactive" // confirmed dead — likely already rotated )