// open source

Small enough to actually read.

A privacy tool you cannot inspect is a promise, not a property. ScrubPony is 2,251 lines of C. You could read all of it this afternoon, and the parts that matter are far shorter than that.

Licence

MIT. Use it, modify it, ship it inside something else, sell that something else. The only condition is the notice.

Where to start reading

PathWhat is in it
src/policy.cThe whole keep/drop decision, as one switch statement over identified segment kinds. If you read one file, read this one.
src/rewrite.cThe copy. Where the entropy-coded scan is passed through untouched.
src/io.cThe write-then-fsync-then-rename, the permission carry-over, and the byte comparison that leaves unchanged files alone.
src/jpeg.cThe segment parser: markers, lengths, and the identifier prefix each payload is labelled with.
src/exif.cThe EXIF reader, and the 36-byte orientation block that is the only metadata this program writes.
src/walk.cDirectory traversal: sorted entries, symlink refusal, depth limit, output and temporary skipping.
tests/verify_scrub.pyThe four-way verification, with its own independent SOS scanner.
tests/fixtures/synthetic/13 committed JPEGs carrying real metadata written by real tools. Safe to publish — everything in them is fabricated.
.github/workflows/ci.ymlThe CI matrix: both platforms, both compilers, every push, plus a timed libFuzzer job.

Boring on purpose

There is no cryptography here and no cleverness either. ScrubPony parses a container format that has been stable since the early nineties, applies a table, and copies bytes. The interesting decisions are all about what not to do: not decoding, not re-encoding, not guessing at a segment's meaning from its marker number, not keeping something merely because it was not recognised.

C99 against POSIX.1-2008, no dependencies beyond libc, no configure step. That is partly taste and partly the point: a tool with no dependency tree is a tool whose whole attack surface is in one repository.

What "verified" means here

  • 741 checks across 7 test binaries plus 8 integration suites, every binary under AddressSanitizer and UndefinedBehaviorSanitizer.
  • 7,575,421 libFuzzer executions against the read path, zero crashes and zero leaks.
  • 30 SIGKILLs mid-write on a 24 MB in-place scrub, zero partial files.
  • 13/13 fixtures agreeing with exiftool — an independent implementation, not a second opinion from the same code.
  • Zero warnings under -Wall -Wextra -Wpedantic -Wconversion, gcc and clang, macOS and Linux.

Reproduce all of it with make check, or check the single strongest claim in two commands from the verification page.

Contributing

Bug reports are welcome, particularly ones that come with the file that caused them — a JPEG this misreads is worth more than a description of how it misreads it. Strip it first if it is yours.

If you are changing the parser or the policy, make check has to pass on both platforms, and new behaviour needs a test that fails without it. Everything in this project that turned out to be wrong was found by a test rather than by reading, so that is not a formality.

The rest of the family

Across the Pony family the crypto cores are open on GitHub under norsehorse-dev, with more on the way. ScrubPony is the one that is public in full, because there is nothing in it that needs to be held back.

Security findings

Send them to NorseHorse@norsehor.se, encrypted via the PGP key if you prefer. The guarantees page documents the full model in the open, including what it deliberately does not defend against.