Fail the build, not the person.
A repository that accepts screenshots and photographs will eventually accept somebody's GPS coordinates, and it will accept them from a contributor who had no idea. A check that runs before the merge is cheaper than a rewrite of history afterwards.
The problem with images in a repository
Documentation screenshots, marketing photographs, test fixtures, avatars. They arrive from many people with many cameras, and a JPEG straight off a phone carries coordinates accurate to a few metres. Once it is in the history it is in every clone, every fork, and every mirror, and removing it means rewriting history and asking everyone to re-clone.
The same applies to any upload pipeline: user-submitted images that go to a public bucket are a metadata leak with your name on it.
What ScrubPony changes
--checkis the whole feature. It reports and changes nothing, is quiet about clean files, and puts the verdict in the exit code: 0 clean, 2 dirty, 1 something went wrong, 64 you got the flags wrong.- An error outranks a dirty verdict. A run that both found metadata and hit an unreadable file exits 1, because the unreadable file is the thing the pipeline needs to know about first.
- It does not report clean files as dirty.
--checkcounts identifying segments, so a photo scrubbed once does not fail forever because of the 36-byte orientation block ScrubPony itself wrote back. A gate that cries wolf gets switched off within a week. - It is fast enough to just run on everything. 1,200 files in about 3.2 seconds, reading only the segments before the scan.
- Nothing to install but a compiler. C99, POSIX.1-2008, no dependencies beyond libc, no configure step. That is a small ask of a CI image.
- The read path is fuzzed. Every input it reads was written by somebody else, so the parser has seen 7,575,421 libFuzzer executions with zero crashes and zero leaks, and every test binary runs under ASan and UBSan.
A workable pattern
- Pre-commit hook: check only the staged images, so it stays fast on a large repository, and print the exact fix command on failure.
- CI:
scrubpony --check -r .as a step. Cheap enough that scanning everything is simpler than working out what changed. - Upload pipeline:
scrubpony -ion the received file before it is stored, or--checkand reject, depending on whether you would rather clean or refuse. - Fix command:
scrubpony -i path/to/file.jpg, which is atomic and preserves permissions.
The full hook and CI snippets are in the CI guide.
Honest limits
It handles JPEG, PNG, WebP and HEIC — which covers the screenshots and photographs most repositories accumulate — but not SVG, RAW, TIFF, AVIF or anything non-image, which are reported and skipped. JPEG is the most weathered path; PNG, WebP and HEIC are newer, each tested and fuzzed but not yet cross-verified to JPEG's depth. If your images are mostly SVG, this gate will pass files it did not inspect, and you should know that rather than assume coverage.
It does not scan git history, only the working tree. A coordinate already committed stays committed until somebody rewrites history.
It removes container metadata, not information: a screenshot with a customer name visible in it passes --check cleanly. And there is deliberately no policy file — if you need a different keep/drop table, this is not the tool, because a configuration somebody edited once and forgot is the fastest route back to leaking GPS.
No Windows. The safety guarantees rest on POSIX semantics that differ there, so a Windows CI runner is out of scope by design rather than by neglect.
Common questions.
Should the hook scrub automatically or fail?
Failing makes the author look at the file, which is often the right outcome — a photograph with GPS in it may also have a street sign in it, and only a person can decide about that. Auto-scrubbing is defensible for machine-generated images where nobody will look either way.
How slow is it on a large repository?
A 1,200-file nested library takes about 3.2 seconds. It reads the segments before the scan and stops; it never decodes an image.
Can I run it on a Windows runner?
No. O_NOFOLLOW, POSIX permission bits and rename-over-an-existing-file all behave differently there, and those are exactly what the guarantees rest on. Use a Linux container.
What about PNG screenshots?
Covered. PNG is supported now, along with WebP and HEIC, so the screenshots that fill most repositories are inspected rather than skipped. JPEG remains the most weathered path; PNG, WebP and HEIC are newer, each with its own test suite and fuzzing. SVG, which is XML rather than a raster container, is still out of scope and skipped.
Get ScrubPony
Strips identifying metadata out of your photos — JPEG, PNG, WebP and HEIC — without touching a pixel. A C99 command line tool for macOS and Linux, and a native Android app on the same core.