patty

command module
v0.4.3 Latest Latest
Warning

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

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

README

patty

patty

Finds leaked GitHub tokens in every corner of a repository's history.
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

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 GitHub to revoke the live ones, after confirmation
patty acme --json > leaks.json   # machine-readable report

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. 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 GitHub which tokens are still active; --revoke then revokes those, after asking (--yes skips the question)
  • --ignore fp,fp -- leave tokens you have already dealt with out of the report, by fingerprint
  • --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

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 GitHub tokens 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. Classic tokens are confirmed against their built-in checksum, so a ghp_ lookalike in a test fixture is not reported.
  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. How patty works has the details, the token families 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 help           # Show all available targets

Test tokens are constructed at runtime from a random part plus a computed checksum, so no token-shaped string is committed to this repository.

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 GitHub credentials in arbitrary byte content.
Package detect finds GitHub credentials in arbitrary byte content.
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 GitHub tokens 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 GitHub tokens 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 and attributes what it finds to commits, paths and refs.
Package scan runs the detector over every object of a repository and attributes what it finds to commits, paths and refs.
source
Package source turns command line arguments into scan targets: local repositories, single GitHub repositories, or every repository of a user or organization.
Package source turns command line arguments into scan targets: local repositories, 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