Documentation
¶
Overview ¶
Package secretscan is WB's deterministic secret-shape gate for agent-authored continuation text (wb session move's handover and wb session park's continuation). It exists because that text is written by an AI, persisted, handed to another agent, and eventually pushed -- so anything in it is effectively published. Guidance alone is not trusted here because its failure is silent: a leaked key in a continuation reads exactly like a good continuation.
WB does not maintain its own secret-shape corpus. It parses gitleaks' maintained TOML rule format (https://github.com/gitleaks/gitleaks) -- vendored at gitleaks/gitleaks.toml, see gitleaks/PROVENANCE.md for the exact pinned tag and how to refresh it without a WB release -- and applies its own fail-closed/warn-only policy on top. WB owns the integration and the blocking decision, never the regex corpus.
Index ¶
Constants ¶
const ExtraRulesEnvVar = "WB_SECRETSCAN_RULES"
ExtraRulesEnvVar names an additional gitleaks-schema rules file to load on top of the embedded baseline. Set it to point at a refreshed config/gitleaks.toml (see gitleaks/PROVENANCE.md) to pick up new upstream patterns without a WB release, or at a small file of internal-only token shapes.
Variables ¶
This section is empty.
Functions ¶
func EmbeddedRuleset ¶
func EmbeddedRuleset() []byte
EmbeddedRuleset returns the byte-for-byte vendored gitleaks ruleset WB ships inside its own binary, so the gate always works even with no network access and no external config file. See gitleaks/PROVENANCE.md for exact provenance.
func Fingerprint ¶
Fingerprint identifies a matched secret without ever reproducing any of its characters: a scanner that prints the secret it found has moved the leak, not stopped it. It is a truncated SHA-256 digest of the exact matched bytes plus their length, e.g. "sha256:4f9c2a1b len=41" -- stable across runs (so the same finding produces the same fingerprint, letting --override-secret name it exactly) and useless for reconstructing the input.
Deliberately not "first 4 chars + length": for several of the named patterns this gate blocks on, the first characters ARE the identifying public prefix (e.g. "AKIA", "ghp_"), but for others they overlap the secret material itself, and a reviewer of refusal output should never have to reason about which case applies. A hash carries zero raw characters in every case.
func FormatRefusal ¶
FormatRefusal renders a fail-closed refusal for one or more blocking findings. It never contains the matched value -- only rule IDs, locations, and Fingerprint's redacted digest -- and always states how to proceed, because an agent that cannot tell why it was blocked will work around the check instead of fixing the input.
func UserRulesPath ¶
UserRulesPath returns the default user-level extra rules path: <config dir>/wb/secretscan/rules.toml. It never errors when the config directory cannot be determined -- it just reports found=false, since this path is optional.
Types ¶
type Finding ¶
type Finding struct {
RuleID string
Description string
Severity Severity
Segment string
Line int
Column int
Fingerprint string
Source string
}
Finding is one rule match. It never carries the matched bytes: Fingerprint is the only trace of what was found, see Fingerprint.
func (Finding) Key ¶
Key identifies this exact occurrence for --override-secret matching: "<rule-id>:<fingerprint-digest>". Line/column are deliberately excluded so an override survives immaterial reflow of surrounding text, but a different secret (different bytes, different fingerprint) always needs its own explicit override.
type LoadOptions ¶
type LoadOptions struct {
// EnvExtraRulesPath overrides ExtraRulesEnvVar's value, for tests.
// Empty means "read the real environment variable".
EnvExtraRulesPath *string
// UserConfigDir overrides the user-level config directory (normally
// os.UserConfigDir), for tests.
UserConfigDir string
}
LoadOptions controls where LoadDefault looks for the operator-extensible rules file, beyond the WB binary's embedded baseline.
type Overrides ¶
Overrides is a set of explicitly acknowledged findings, keyed by Finding.Key ("<rule-id>:<fingerprint-digest>"). It downgrades one exact occurrence from block to warn -- never a rule ID alone, and never a blanket "skip scanning" switch. A different secret produces a different fingerprint and therefore needs its own explicit override, so this can never be reused as a standing bypass.
Overrides must be constructed from caller-supplied --override-secret values (see ParseOverrides), which in turn can only be produced by first running into the refusal and reading its printed fingerprint. That sequencing -- fail, read, decide, re-run with the exact key -- is the point: an override is explicit and effortful, never the path of least resistance.
func ParseOverrides ¶
ParseOverrides parses repeatable "<rule-id>:<fingerprint-digest>" CLI values, e.g. "aws-access-token:sha256:4f9c2a1b", as produced verbatim by Finding.Key. Every value must be well-formed; a malformed override is a hard error rather than a silently ignored no-op, because a bypass an agent believes is armed but is not would defeat its own purpose.
type Result ¶
type Result struct {
Findings []Finding
}
Result is every finding from one Scan call.
type Rule ¶
type Rule struct {
ID string
Description string
Regex *regexp.Regexp
// Keywords are a cheap case-insensitive substring pre-filter: if none of
// them appear in the scanned content, the (potentially expensive) regex
// is skipped. An empty Keywords list means always evaluate the regex.
Keywords []string
Severity Severity
// Source names where this rule came from, for refusal messages and
// audit trails: "gitleaks-embedded", or the loaded config file path.
Source string
}
Rule is one compiled secret-shape detector.
type Scanner ¶
type Scanner struct {
// contains filtered or unexported fields
}
Scanner evaluates Rules against text. The zero value is not usable; build one with Load or LoadDefault.
func LoadDefault ¶
func LoadDefault(options LoadOptions) (*Scanner, []string, error)
LoadDefault builds the Scanner WB uses in production: the embedded gitleaks-derived baseline, plus whichever extra rules file is configured (env var first, then the user-level default path), if it exists. A missing extra rules file is not an error -- it is the common case.
func NewScanner ¶
NewScanner builds a Scanner from already-loaded rules, for tests and for callers assembling a custom rule set directly.
type Segment ¶
Segment is one named, independently line-numbered slice of the text under scan, e.g. a park continuation's distinct fields (--summary, --validation, --remaining, the body file) so a refusal can say exactly which one matched.
type Severity ¶
type Severity string
Severity is WB's own policy classification for a rule, not anything gitleaks declares. See policy.go for how it is assigned.
const ( // SeverityBlock rules refuse the operation outright: fail closed. Every // rule loaded from the vendored/embedded ruleset is Block unless it is // named in heuristicRuleIDs. SeverityBlock Severity = "block" // SeverityWarn rules are reported but never refuse. Reserved for // entropy-driven heuristics that lack a fixed, brand-specific shape and // are known to false-positive on hashes, UUIDs, and base64 blobs. SeverityWarn Severity = "warn" )