Documentation
¶
Overview ¶
Package detect finds credentials in arbitrary byte content and knows, per provider, how to verify and revoke them.
The scan is built for throughput: every provider does a handful of SIMD-accelerated substring searches for its fixed token prefixes followed by an exact shape check and, where the format allows it, an offline checksum. A well-formed string with a wrong checksum is not a token; that single check removes the false positives a regex scanner has to live with.
The shared vocabulary lives here: Kind, Token, Verification and the Provider interface. The providers themselves are sub-packages, assembled into a Registry by package providers.
Index ¶
- Constants
- func All(b []byte, ok func(byte) bool) bool
- func AlnumAt(content []byte, i int) bool
- func Base64URLAt(content []byte, i int) bool
- func Child(m *yaml.Node, key string) *yaml.Node
- func Do(client *http.Client, req *http.Request) (*http.Response, error)
- func Fingerprint(value string) string
- func HasKind(content []byte, kind string) bool
- func HintMatches(hint, value string) bool
- func InSecret(ref, key, attribution string) string
- func IsAlnum(c byte) bool
- func IsBase64URL(c byte) bool
- func IsDigit(c byte) bool
- func IsHex(c byte) bool
- func ReadBody(r io.Reader, limit int64) []byte
- func Redact(value string) string
- func Scalar(m *yaml.Node, key string) string
- func Span(content []byte, start, limit int, ok func(byte) bool) int
- func WordBefore(content []byte, i int) bool
- type CommitterCorrelator
- type Configurable
- type Correlator
- type Document
- type DryRunRevoker
- type Kind
- type KindInfo
- type LocalSources
- type PathClassifier
- type Provider
- type ProximityCorrelator
- type Registry
- func (r *Registry) AllowPrivateServers(allow bool)
- func (r *Registry) Correlators() []Correlator
- func (r *Registry) Find(content []byte) []Token
- func (r *Registry) Info(kind Kind) KindInfo
- func (r *Registry) Observe(content []byte) []Sighting
- func (r *Registry) Provider(kind Kind) Provider
- func (r *Registry) ProviderName(kind Kind) string
- func (r *Registry) Providers() []Provider
- func (r *Registry) Revocable(kind Kind) bool
- func (r *Registry) RevokePage(kind Kind) string
- func (r *Registry) Verify(ctx context.Context, tok Token) Verification
- type Secret
- type SecretValue
- type ServerVerifier
- type Sighting
- type Token
- type Verification
- type VerifyStatus
Constants ¶
const UserAgent = "patty"
UserAgent identifies patty to the provider APIs.
Variables ¶
This section is empty.
Functions ¶
func AlnumAt ¶ added in v0.5.0
AlnumAt reports whether content has an alphanumeric byte at i; false past the end. Used to reject candidates that continue into a longer word.
func Base64URLAt ¶ added in v0.8.0
Base64URLAt reports whether content has a URL-safe base64 byte at i; false past the end. Used to reject candidates that continue into a longer key.
func Fingerprint ¶
Fingerprint returns the fingerprint of a raw token value.
func HasKind ¶ added in v0.10.0
HasKind reports whether content spells `kind: <kind>` somewhere, in YAML or JSON, with or without quotes: the cheap test before parsing anything.
func HintMatches ¶ added in v0.8.0
HintMatches reports whether a partially redacted credential, the head and tail of the value around an ellipsis as providers list their keys (`sk-ant-api03-R2D…igAA`, `sk-abc...def`), fits the full value. A hint without an ellipsis or without a tail never matches: the head alone is usually just the prefix every key shares.
func InSecret ¶ added in v0.10.0
InSecret prefixes a credential's attribution with the Secret manifest and key it was found under.
func IsBase64URL ¶ added in v0.8.0
IsBase64URL reports whether c is in the URL-safe base64 alphabet, which most API keys are made of.
func Redact ¶
Redact hides the middle of a credential, keeping enough of both ends to recognise it (`ghp_AbCd…WxYz`). A URL keeps its path up to the last segment, which is the secret part of a webhook (`https://…/T…/B…/Ab…Yz`).
func Span ¶ added in v0.5.0
Span returns the length of the run of bytes starting at content[start] that satisfy ok, at most limit bytes.
func WordBefore ¶ added in v0.8.0
WordBefore reports whether the byte before position i continues a word into the candidate: a letter, digit or underscore. False at the start.
Types ¶
type CommitterCorrelator ¶ added in v0.13.0
type CommitterCorrelator interface {
Correlator
// Committers is handed the distinct GitHub logins of a repository's
// authors and contributors and returns, per identifier (in the form
// Identifiers reports them), what a match means: "matches octocat's
// GitHub SSH key". It may fetch public data and is called once per
// repository, after the scan, only when the repository holds a
// credential of this provider.
Committers(ctx context.Context, logins []string) map[string]string
}
CommitterCorrelator is a Correlator that also relates its credentials to the people who committed to a scanned repository, through what the hosting service publishes about them: the SSH keys of a GitHub account.
type Configurable ¶ added in v0.8.0
Configurable is a Provider that takes operator configuration from the environment: a privileged credential of the operator's own, such as an organization's admin key, that lets the provider revoke credentials its API would otherwise only accept as callers. Configure runs once, before the provider joins a Registry, so Kinds may depend on what is configured.
type Correlator ¶ added in v0.7.0
type Correlator interface {
// Observe returns the identifiers under which content names credentials
// of this provider (the recipients an encrypted file lists), or nil when
// the object is of no interest. It runs on every scanned object and has
// to be cheap; it must not keep a reference to content.
Observe(content []byte) []Sighting
// Identifiers returns what the credential is known as in such content:
// the public key of an identity. The scan matches them against what
// Observe reported.
Identifiers(tok Token) []string
}
Correlator is a Provider whose credentials unlock content that may sit in the scanned repositories themselves: an age identity decrypts every sops file encrypted to its recipient. The scan shows it every object and asks afterwards what each credential opens, so the report can say how much a leak is worth.
type Document ¶ added in v0.10.0
type Document struct {
Body []byte
// Start is the byte offset of Body in the content it was cut from.
Start int
// contains filtered or unexported fields
}
Document is one YAML document of a stream, and where it starts in the scanned content. JSON is YAML, so a JSON object is one Document.
func Documents ¶ added in v0.10.0
Documents splits a YAML stream at its `---` separators, so a document that does not parse costs only itself.
type DryRunRevoker ¶ added in v0.5.0
DryRunRevoker is a Provider whose revocation endpoint can rehearse a revocation: it answers as it would for the real request without revoking anything. patty uses it to preview a revocation before asking for confirmation.
type Kind ¶
type Kind string
Kind names one credential family of one provider, such as a classic GitHub personal access token or a Slack bot token.
type KindInfo ¶ added in v0.5.0
type KindInfo struct {
Kind Kind
// Description is the human name: "personal access token (classic)".
Description string
// Revocable reports whether the provider's revocation endpoint accepts this kind.
Revocable bool
// RevokePage is where the owner revokes a credential of this kind by hand.
RevokePage string
// RevokeNote is added to the manual revocation advice when the API cannot
// revoke the kind: what to check instead, or why it does not matter.
RevokeNote string
// RevokeEffect names a side effect of revoking through the API that is
// easy to miss. The placeholder {app} stands for the issuing application.
RevokeEffect string
// AuditNote says how to find out whether a leaked credential of this kind
// was used while it was exposed, and what the provider does on its own
// when it spots the leak. Shown for every finding, revoked or not.
AuditNote string
// UnlocksLabel labels the advice line that lists what a credential of
// this kind opens in the scanned repositories ("decrypts"). Set only for
// the kinds of a Correlator.
UnlocksLabel string
// UnlocksNone is that line when nothing scanned names the credential.
UnlocksNone string
// PublicValue reports that Token.Value names the credential without
// revealing it, such as a registry login's host and username whose
// secret is the password in Token.Secret; the report shows it in full.
PublicValue bool
// Opaque reports that a finding of this kind names secret material of
// no recognised shape, a plaintext Kubernetes Secret, which no provider
// can verify. The report lists such findings after every credential it
// can put a name to.
Opaque bool
}
KindInfo describes one credential family.
type LocalSources ¶ added in v0.5.0
type LocalSources struct {
// Env lists environment variables tools read the credential from.
Env []string
// EnvFiles lists environment variables whose value is the path of a
// file holding the credential, or a list of such paths separated the
// way PATH is.
EnvFiles []string
// ConfigFiles are relative to the XDG config directory (~/.config).
ConfigFiles []string
// HomeFiles are relative to the home directory.
HomeFiles []string
// Commands print a credential to stdout, such as `gh auth token`.
Commands [][]string
}
LocalSources names where a provider's credentials are configured on the machine running patty. Paths may contain globs.
type PathClassifier ¶ added in v0.13.0
type PathClassifier interface {
// Classify returns the kind for a credential found under paths and
// matched by the identifiers in matched, or "" to keep the kind Find
// gave it. The scan applies it after attribution and correlation.
Classify(tok Token, paths []string, matched []string) Kind
}
PathClassifier is a Provider whose kinds are told apart by where a credential lives and what names it rather than by its shape alone: a PEM private key is an SSH key in id_rsa or when an authorized_keys file lists its public key, and a TLS key in server.key.
type Provider ¶ added in v0.5.0
type Provider interface {
// Name is how the provider is called in reports: "GitHub", "Slack".
Name() string
// Kinds describes every credential family the provider detects.
Kinds() []KindInfo
// Find returns every credential of this provider in content. Offsets
// are set; line numbers are filled in by the Registry.
Find(content []byte) []Token
// Verify asks the provider whether the credential is still accepted.
// Only the provider's explicit invalid-credentials answer is reported as
// revoked; anything else that is not a clean acceptance is unknown.
Verify(ctx context.Context, tok Token) Verification
// Revoke asks the provider to revoke the given credentials. A nil error
// means every request was accepted; the caller confirms the outcome with
// Verify.
Revoke(ctx context.Context, tokens []Token) error
// LocalSources lists where tools keep this provider's credentials on a
// developer machine.
LocalSources() LocalSources
}
Provider is one credential issuer: it knows the token formats it hands out, how to ask whether one is still live, and how to revoke it.
A provider never stores or logs a token value, and never contacts its API from Find.
type ProximityCorrelator ¶ added in v0.13.0
type ProximityCorrelator interface {
Correlator
// Adjacent returns the identifiers a credential of this kind adopts
// from a sighting found in the same directory, or nil when the
// sighting is not one it should.
Adjacent(kind Kind, s Sighting) []string
}
ProximityCorrelator is a Correlator with credentials that say nothing about their public half, an encrypted cosign key, so they are related to the sightings in the directory they were found in instead: the cosign.pub next to cosign.key.
type Registry ¶ added in v0.5.0
type Registry struct {
// contains filtered or unexported fields
}
Registry is the set of providers a scan looks for. It dispatches by Kind and merges the providers' findings into one offset-ordered list.
func NewRegistry ¶ added in v0.5.0
NewRegistry builds a registry from providers, in report order.
func (*Registry) AllowPrivateServers ¶ added in v0.10.0
AllowPrivateServers tells every provider that verifies against servers named in the scanned content whether private, loopback and link-local addresses may be contacted.
func (*Registry) Correlators ¶ added in v0.7.0
func (r *Registry) Correlators() []Correlator
Correlators returns the registered providers that relate their credentials to the content they unlock, in order.
func (*Registry) Find ¶ added in v0.5.0
Find returns every credential of every provider in content, sorted by offset, with line numbers filled in. The values of a Kubernetes Secret manifest are decoded and searched too: a credential found inside one is placed at the line of its key and attributed to the Secret.
func (*Registry) Info ¶ added in v0.5.0
Info describes the kind; the zero KindInfo for an unknown one.
func (*Registry) Observe ¶ added in v0.13.0
Observe shows content to every correlating provider and returns what they saw. The decoded values of the Kubernetes Secret manifests in content are shown too, so a certificate committed as the tls.crt of a Secret names the key it belongs to like one committed as a file.
func (*Registry) Provider ¶ added in v0.5.0
Provider returns the provider that issues credentials of this kind, or nil.
func (*Registry) ProviderName ¶ added in v0.5.0
ProviderName returns the name of the provider of this kind, or "".
func (*Registry) Revocable ¶ added in v0.5.0
Revocable reports whether the kind's provider revokes it through its API.
func (*Registry) RevokePage ¶ added in v0.5.0
RevokePage is where the owner revokes a credential of this kind by hand.
type Secret ¶ added in v0.10.0
type Secret struct {
Namespace string
Name string
// Type is the Secret's type, "" for Opaque.
Type string
// Offset is where the manifest's kind is written in the content.
Offset int
// Sops reports that the manifest carries a sops metadata block: its
// values are ciphertext and nothing about them is a finding.
Sops bool
Values []SecretValue
}
Secret is one Kubernetes Secret manifest found in scanned content, with every value it carries decoded: the base64 under data and the clear text under stringData. A manifest is a leak whatever its values look like, so the parse keeps every key and says why a value was not decoded.
func Secrets ¶ added in v0.10.0
Secrets finds every Kubernetes Secret manifest in content: YAML or JSON, one document or several separated by `---`, on their own or as items of a List. Content that does not spell `kind: Secret` costs one substring search. A document that does not parse, a Helm template with bare `{{` blocks, is skipped rather than guessed at.
func (Secret) Plaintext ¶ added in v0.10.0
func (s Secret) Plaintext() []SecretValue
Plaintext returns the values that hold material in the clear: decoded and not skipped.
type SecretValue ¶ added in v0.10.0
type SecretValue struct {
Key string
// Offset is where the key is written in the content, which is where a
// finding inside the value is placed; End is where the next key, or the
// document, starts. A provider's own finding between the two sits in
// this value.
Offset, End int
// Value is the decoded material, nil when Skipped says why not.
Value []byte
// Skipped names the reason a value was left alone: empty, templated,
// encrypted (a sops ENC[…] value), not base64, or too large.
Skipped string
}
SecretValue is one entry of a Secret's data or stringData map.
type ServerVerifier ¶ added in v0.10.0
type ServerVerifier interface {
AllowPrivateServers(allow bool)
}
ServerVerifier is a Provider that verifies credentials against servers named in the scanned content itself, the API server of a kubeconfig, rather than a fixed public API. Such a provider refuses servers on private, loopback and link-local addresses unless told otherwise: a repository must not be able to point patty at the operator's own network.
type Sighting ¶ added in v0.13.0
type Sighting struct {
ID string
// Detail is shown next to the file that names the credential; empty
// when the file itself says enough (a sops recipient list).
Detail string
}
Sighting is one identifier a Correlator saw in scanned content, and what the content says about it when the identifier alone does not: the names and expiry of the certificate a public key belongs to.
type Token ¶
type Token struct {
Kind Kind
Value string
// Offset is the byte offset of the token in the scanned content.
Offset int
// Line is the 1-based line the token starts on.
Line int
// ChecksumVerified reports whether the token carries a checksum that
// was verified offline. Classic GitHub tokens do; every other format is
// matched on shape alone.
ChecksumVerified bool
// Attribution is what the token's own shape says about its owner, without
// contacting the provider: a team id in a Slack token, for example. Empty
// when the format carries nothing of the sort.
Attribution string
// Encrypted reports that the material is passphrase-protected and so
// useless to whoever found it unless the passphrase leaked with it: an
// encrypted private key. The report lists such findings after the ones
// that are usable as they are.
Encrypted bool
// Secret is the material a credential needs besides Value when it is made
// of several strings: the secret access key found next to an AWS key id,
// and the session token of a temporary one. Its layout is the provider's
// business. Value alone identifies the credential; Secret is never
// printed, logged or fingerprinted.
Secret string
}
Token is one credential found in scanned content.
func ScanPrefix ¶ added in v0.5.0
func ScanPrefix(found []Token, content []byte, prefix string, at func(start int) (Token, bool)) []Token
ScanPrefix finds every occurrence of prefix in content and calls at with its offset; at returns the token when the bytes there have the exact shape. Matches are appended to found.
func (Token) Fingerprint ¶
Fingerprint returns a short, stable, non-reversible identifier for the token value: the first 16 hex characters of its SHA-256. It is safe to put in logs and allow-lists.
type Verification ¶
type Verification struct {
Status VerifyStatus `json:"status"`
// Detail describes what the credential gives access to: user and scopes,
// the workspace of a Slack token, the number of repositories an
// installation token reaches.
Detail string `json:"detail,omitempty"`
// ClientID is the id of the application the credential was issued to,
// when the provider reports one.
ClientID string `json:"client_id,omitempty"`
// App is the name of that application when patty knows the client id.
App string `json:"app,omitempty"`
// Expires is when the credential stops working, for credentials that expire.
Expires string `json:"expires,omitempty"`
}
Verification is the result of Provider.Verify.
func (Verification) Issuer ¶ added in v0.2.0
func (v Verification) Issuer() string
Issuer names the application a credential was issued to: the known app name, else the raw client id, else "".
type VerifyStatus ¶
type VerifyStatus string
VerifyStatus is the outcome of checking a credential against its provider.
const ( // StatusActive means the provider accepted the credential: it is live and must be revoked. StatusActive VerifyStatus = "active" // StatusRevoked means the provider explicitly rejected the credential as // invalid, or the credential itself says it can no longer be accepted: // a certificate or token past the expiry it carries. StatusRevoked VerifyStatus = "revoked" // StatusUnverifiable means the credential family cannot be checked without side effects. StatusUnverifiable VerifyStatus = "unverifiable" // StatusUnknown means the check could not be completed (network, rate // limit, an unexpected answer). It is never treated as proof of anything. StatusUnknown VerifyStatus = "unknown" )
Directories
¶
| Path | Synopsis |
|---|---|
|
Package anthropic is the Anthropic credential provider: API keys, Admin API keys, and the OAuth access and refresh tokens Claude Code signs in with.
|
Package anthropic is the Anthropic credential provider: API keys, Admin API keys, and the OAuth access and refresh tokens Claude Code signs in with. |
|
Package aws is the AWS credential provider: the access keys of IAM users and the temporary access keys STS hands out.
|
Package aws is the AWS credential provider: the access keys of IAM users and the temporary access keys STS hands out. |
|
Package azure is the Microsoft Azure credential provider: the client secrets of Entra ID applications (service principals), storage account keys, and shared access signatures.
|
Package azure is the Microsoft Azure credential provider: the client secrets of Entra ID applications (service principals), storage account keys, and shared access signatures. |
|
Package gcp is the Google Cloud credential provider: service account keys, the application default credentials of a signed-in user, the OAuth access and refresh tokens they produce, and API keys.
|
Package gcp is the Google Cloud credential provider: service account keys, the application default credentials of a signed-in user, the OAuth access and refresh tokens they produce, and API keys. |
|
Package github is the GitHub credential provider: classic and fine-grained personal access tokens, OAuth and GitHub App tokens.
|
Package github is the GitHub credential provider: classic and fine-grained personal access tokens, OAuth and GitHub App tokens. |
|
Package jwt reads JSON Web Tokens without verifying them.
|
Package jwt reads JSON Web Tokens without verifying them. |
|
Package kubernetes is the provider for the credentials that reach a Kubernetes API server: the client certificates, bearer tokens and basic auth logins a kubeconfig carries, service account tokens wherever they turn up, and Secret manifests committed with their values in the clear.
|
Package kubernetes is the provider for the credentials that reach a Kubernetes API server: the client certificates, bearer tokens and basic auth logins a kubeconfig carries, service account tokens wherever they turn up, and Secret manifests committed with their values in the clear. |
|
Package openai is the OpenAI credential provider: project, service account, admin and legacy user API keys.
|
Package openai is the OpenAI credential provider: project, service account, admin and legacy user API keys. |
|
Package privatekey is the provider for private keys committed as PEM blocks: SSH keys, the keys behind TLS certificates and other PKCS#8 or legacy PEM material, and the encrypted signing keys cosign writes.
|
Package privatekey is the provider for private keys committed as PEM blocks: SSH keys, the keys behind TLS certificates and other PKCS#8 or legacy PEM material, and the encrypted signing keys cosign writes. |
|
Package providers assembles the credential providers patty ships with.
|
Package providers assembles the credential providers patty ships with. |
|
Package registry is the provider for OCI and Docker registry credentials: the logins a Docker config keeps per registry, wherever that config is embedded (a config.json, a Kubernetes pull secret, Helm values, a Basic Authorization header aimed at a registry), and the native tokens of Docker Hub and Quay.
|
Package registry is the provider for OCI and Docker registry credentials: the logins a Docker config keeps per registry, wherever that config is embedded (a config.json, a Kubernetes pull secret, Helm values, a Basic Authorization header aimed at a registry), and the native tokens of Docker Hub and Quay. |
|
Package slack is the Slack credential provider: bot, user, app-level, refresh and configuration tokens, and incoming webhook URLs.
|
Package slack is the Slack credential provider: bot, user, app-level, refresh and configuration tokens, and incoming webhook URLs. |
|
Package sops is the provider for the identities that decrypt sops-managed secrets: age identities and PGP private keys.
|
Package sops is the provider for the identities that decrypt sops-managed secrets: age identities and PGP private keys. |