patty

command module
v0.15.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 3 Imported by: 0

README

patty

patty

Finds leaked credentials in every corner of a repository's history, including the commits a clone never shows.
Marge's sister. Works at the DMV. Checks everyone's credentials.


A token that was committed once is in the repository forever -- even after the file was deleted, the commit amended, the branch force-pushed or the pull request closed. A normal clone does not show most of that history, and a scanner that walks git log --all never sees it. patty does: it mirrors the whole object database, pulls in what GitHub still holds but no ref points at anymore, and scans every object once.

It is fast enough to point at an entire organization. 210,000 objects and 6.7 GiB of content of prometheus/prometheus scan in about two seconds; the wall clock is the clone.

Install

Download a binary from the releases page (Linux, macOS, Windows; amd64 and arm64), or build from source:

go install github.com/teemow/patty@latest

patty drives git on the command line, so git 2.30 or newer has to be on the PATH.

Setup

For GitHub targets patty uses a token from GITHUB_TOKEN, GH_TOKEN, or the gh CLI (gh auth token), in that order. Without one it works anonymously: public repositories only, 60 API requests per hour.

Classic token: repo scope for private repositories, nothing for public ones.

Fine-grained token: select the repositories to scan, then grant:

Permission Access Why
Metadata Read List repositories, read sizes and the activity feed
Contents Read Clone private repositories

Anthropic and OpenAI keys: their APIs cannot revoke a key by itself. To let --revoke deactivate a leaked Anthropic API key or delete a leaked OpenAI key, set ANTHROPIC_ADMIN_KEY to an Admin API key of the organization the leaked key belongs to, or OPENAI_ADMIN_KEY to an admin key of that organization. With one configured, --verify also names each key the way the Console does (name, workspace or project, creator), and --revoke deactivates only keys the organization's own key list confirms as its own; a key from another organization is reported as such. Without one, the report says where to revoke by hand. Detection and --verify need no admin key.

Google Cloud: the file GOOGLE_APPLICATION_CREDENTIALS names, CLOUDSDK_AUTH_ACCESS_TOKEN, and the credential files gcloud writes under ~/.config/gcloud are read locally, so the report can say that a leaked service account key or refresh token is still configured on this machine; only fingerprints are compared, and gcloud's credentials.db is not opened. Revoking a Google OAuth token with --revoke signs out every tool that shares its grant, gcloud included.

Grafana and GitLab instances: a Grafana service account token or API key, and every GitLab token, is accepted by one instance the token does not name. With --verify, patty tries the instances passed with --grafana-url and --gitlab-url (repeatable; or GRAFANA_URL and GITLAB_URL, comma-separated), gitlab.com for GitLab, and the Grafana and GitLab hosts the scanned content itself names, the latter over https only and never on a private network unless --verify-private-servers is given. Instances you name are contacted as given. See instances a token does not name.

The tokens patty finds never leave your machine unless you pass --verify or --revoke; see what leaves your machine.

Usage

patty .                          # the repository you are in, reflog and stashes included
patty acme/api acme/web          # two GitHub repositories
patty acme --verify              # everything acme owns; say which tokens are still live
patty acme --revoke              # ...and ask their providers to revoke the live ones, after confirmation
patty acme --json > leaks.json   # machine-readable report
patty ~/downloads                # a directory that is no repository: every file on disk

A target is a local path, owner/repo, a github.com URL, or a bare owner (user or organization) to scan every repository of, private ones included when the token can see them. A local directory that is not a repository, or a single file, is scanned file by file as it is on disk; --files does the same for the working tree of a repository, so the untracked and ignored files a history scan never sees are covered. Exit code 0 means nothing was found, 1 that tokens were found, 2 that a target failed or was skipped and nothing was found.

patty --help lists every flag. The ones you will reach for:

  • --verify -- ask each credential's provider, or the API server a kubeconfig names, which credentials are still active; --revoke then revokes those, after asking (--yes skips the question). API servers on private networks are only contacted with --verify-private-servers
  • --grafana-url, --gitlab-url -- the instances to check Grafana and self-managed GitLab tokens against, besides the ones the scanned content names itself
  • --ignore fp,fp -- leave tokens you have already dealt with out of the report, by fingerprint or by kind (--ignore kubernetes-secret-manifest)
  • --keep -- keep mirrors in the cache so a re-run only fetches what changed; --max-disk and --min-free cap what the cache may use
  • --include-forks -- include forks when expanding an owner
  • --files -- also scan the files of a local repository as they are on disk, .gitignored ones included

Other commands: patty revoke for tokens you already have in hand, patty cache and patty cache clean for kept mirrors, patty self-update.

The report

2 credentials found (1 active) in 108 repositories, 82473 objects, 1.5 GiB

● ACTIVE     github-pat               ghp_2O6PWxYz…k3Lq  fp b492588d8d3ffbbb  user acme-bot, scopes: repo, workflow
    acme/dotfiles  .config/hub:4  2c10f8f4 2025-04-15 Jane Doe · initial commit
                   not on any branch or tag, only reachable through pull request refs: PR #1, PR #10, +11 more
    acme/lab       trials/run-7/messages.json:107  4048b6e8 2026-05-29 Jane Doe · record trial output
                   on main, +405 more
    ↳ revoke   at https://github.com/settings/tokens; or run again with --revoke
    ↳ local    still configured in ~/.config/hub; replace it there after revoking
    ↳ history  acme/dotfiles: only in pull request refs (GitHub Support has to purge those) · acme/lab: in branch history (rewrite with git filter-repo, then force-push)

● revoked    github-pat               ghp_jtP7Ab12…9zXy  fp 58b0d6ffe3821055
    acme/infra     cluster/apps/secret.sops.yaml:8  96a9ac2e 2026-01-05 Jane Doe · add training app
                   orphaned: no branch, tag or PR reaches this commit · force-pushed away from main on 2026-01-06 by jane

Each token is listed once with every place it was found, the oldest commit that introduced it, and how reachable that commit still is: on a branch, only through pull request refs, or orphaned by a force push. Active tokens come first, each with what to do about it: where to revoke it, whether it is still configured on this machine, and what its history needs. Reading the report explains every line, revoking and its side effects included.

How it works

  1. Mirror, not clone. git clone --mirror brings every ref GitHub advertises, including refs/pull/* and so the history of every pull request.
  2. Fetch what was rewritten. The repository activity feed names the commits that were force-pushed away or deleted; GitHub still serves them by SHA, so patty fetches them too.
  3. Scan objects, not diffs. Every blob, commit and tag in the object database is read exactly once, reachable or not, and checked for every credential kind patty knows; the detection table lists them. A format with a built-in checksum is verified offline, so a lookalike in a test fixture is not reported. What a credential's own shape reveals is attributed without asking anyone: the account an AWS key id belongs to, the sops files an age identity decrypts, the certificate, authorized_keys, image policy or GitHub account that trusts a private key's public half. The values of every Kubernetes Secret manifest are decoded and searched too, and a Secret committed in the clear is reported on its own.
  4. Attribute afterwards. Only for objects that contain a token does patty look up the path, the introducing commit, and the refs that still contain it.

Mirrors live in a size-capped cache and are removed after the scan unless --keep is set, so pointing patty at an organization never fills a drive. A directory that is not a repository has no history to mirror; its files are scanned as they are on disk. How patty works has the details, every credential kind it detects, and a comparison with gitleaks.

Development

make build          # Build the binary
make test           # Run tests (needs git on the PATH)
make lint           # Run golangci-lint
make docs           # Regenerate the kinds table in docs/how-it-works.md
make help           # Show all available targets

Test tokens are constructed at runtime, so no token-shaped string is committed to this repository. CONTRIBUTING.md has the rules and a checklist for adding a provider.

License

MIT -- see LICENSE for details.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
Package cmd wires the patty command line.
Package cmd wires the patty command line.
internal
detect
Package detect finds credentials in arbitrary byte content and knows, per provider, how to verify and revoke them.
Package detect finds credentials in arbitrary byte content and knows, per provider, how to verify and revoke them.
detect/anthropic
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.
detect/aws
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.
detect/azure
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.
detect/gcp
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.
detect/github
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.
detect/gitlab
Package gitlab is the GitLab credential provider: personal access tokens and the other token families GitLab prefixes with `gl`: deploy, runner, CI job, pipeline trigger, feed, incoming mail, agent, OAuth application and feature flag tokens.
Package gitlab is the GitLab credential provider: personal access tokens and the other token families GitLab prefixes with `gl`: deploy, runner, CI job, pipeline trigger, feed, incoming mail, agent, OAuth application and feature flag tokens.
detect/grafana
Package grafana is the Grafana credential provider: service account tokens, Grafana Cloud access policy tokens and the API keys Grafana issued before service accounts.
Package grafana is the Grafana credential provider: service account tokens, Grafana Cloud access policy tokens and the API keys Grafana issued before service accounts.
detect/jwt
Package jwt reads JSON Web Tokens without verifying them.
Package jwt reads JSON Web Tokens without verifying them.
detect/kubernetes
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.
detect/npm
Package npm is the npm registry credential provider: the access tokens npm has issued since 2021 (npm_…) and the UUID tokens before them.
Package npm is the npm registry credential provider: the access tokens npm has issued since 2021 (npm_…) and the UUID tokens before them.
detect/oci
Package oci 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 oci 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.
detect/openai
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.
detect/pagerduty
Package pagerduty is the PagerDuty credential provider: REST API keys and the routing keys (integration keys) that send events to a service.
Package pagerduty is the PagerDuty credential provider: REST API keys and the routing keys (integration keys) that send events to a service.
detect/privatekey
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.
detect/providers
Package providers assembles the credential providers patty ships with.
Package providers assembles the credential providers patty ships with.
detect/slack
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.
detect/sops
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.
disk
Package disk keeps patty's clone cache within a byte budget and off the last free gigabytes of the drive.
Package disk keeps patty's clone cache within a byte budget and off the last free gigabytes of the drive.
github
Package github talks to the GitHub API: repository discovery, size estimates for the disk budget and the activity feed that names commits a clone can no longer see.
Package github talks to the GitHub API: repository discovery, size estimates for the disk budget and the activity feed that names commits a clone can no longer see.
gitrepo
Package gitrepo drives git plumbing for a repository patty scans.
Package gitrepo drives git plumbing for a repository patty scans.
localcreds
Package localcreds finds the credentials configured on this machine, so a report can say that a leaked token is not just out there but still in use right here.
Package localcreds finds the credentials configured on this machine, so a report can say that a leaked token is not just out there but still in use right here.
report
Package report renders scan results for terminals and machines.
Package report renders scan results for terminals and machines.
scan
Package scan runs the detector over every object of a repository, or every file of a directory tree, and attributes what it finds to commits, paths and refs.
Package scan runs the detector over every object of a repository, or every file of a directory tree, and attributes what it finds to commits, paths and refs.
source
Package source turns command line arguments into scan targets: local repositories, directories and files, single GitHub repositories, or every repository of a user or organization.
Package source turns command line arguments into scan targets: local repositories, directories and files, single GitHub repositories, or every repository of a user or organization.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL