// support

Something odd?

Most surprises with ScrubPony are one of about ten things, and eight of them are documented behaviour rather than bugs. Start here; if none of it fits, the inbox is at the bottom.

Before anything else: -n

The dry run writes nothing, touches nothing, and prints the decision and reason for every interesting segment plus a summary of the EXIF that is present. Nine times out of ten it answers the question directly:

scrubpony -n the-file-in-question.jpg

For structure with no policy opinion attached, -l lists offsets, markers, payload lengths and identifiers. That is the mode the golden tests compare against exiftool, so it is the one to quote in a bug report.

Common questions.

My photos came out sideways.
Something dropped the EXIF orientation. ScrubPony preserves it by default in a 36-byte block, so check whether --no-keep-orientation or --strict was in the command, or whether another tool touched the file afterwards. Run scrubpony -n on the original: if it says "orientation would be preserved in a 36-byte EXIF block", ScrubPony is not the one that lost it.
--check still reports a file I already scrubbed.
It should not, and this is the case the flag was designed around. --check counts identifying segments: a block that matches the bytes ScrubPony emits, exactly, does not count. If a scrubbed file still reports dirty, the EXIF block on it is not one of ours — something re-added metadata after the scrub, or the file was scrubbed with --no-keep-orientation and then edited. Run scrubpony -l on it to see what is actually there.
It says "not a JPEG, skipping" but the file ends in .jpg.
The extension is not consulted. ScrubPony decides by looking for the SOI marker at the start of the file, so a .jpg that is really a PNG, a HEIC, or a download error is correctly reported as not a JPEG. Run file on it to confirm.
It refused a symbolic link.
Deliberately, and even one named directly on the command line. A symlink loop is an infinite walk and a symlink out of the tree is a way to modify files you did not mean to touch. Point it at the real path instead.
"pick an output directory outside the tree being scrubbed"
The -d target and a source path overlap in one direction or the other. An output directory inside the source means the second run reads what the first one wrote; a source inside the output means scrubbing the scrubbed copies. Both are loops that look like progress, so both are refused. Use a target elsewhere.
My Finder tags disappeared.
That is -i, and it is documented rather than hidden. Extended attributes live on the inode, and replacing a file installs a new one, so Finder tags, Spotlight comments and quarantine flags go with the old inode. Hard links break for the same reason. Use the default sibling output or -d and move the results into place yourself if those matter.
The modification times all changed.
Deliberate. The mtime is metadata too, and a scrubbed file still carrying its original capture time defeats part of the purpose. There is no flag to keep it.
make fuzz fails on macOS.
Apple's clang does not ship libFuzzer. Either brew install llvm and build with make fuzz CC=$(brew --prefix llvm)/bin/clang, or use make fuzz-dumb, a self-contained mutation fuzzer that needs nothing but a C compiler.
make check fails complaining about sanitizers.
Some toolchains cannot provide them — Homebrew GCC on macOS is the usual case, where AddressSanitizer belongs to Apple's runtime rather than GCC's. Run make SAN= check to build the suite without them.
Some tests report as skipped.
Expected, and not a failure. The exiftool cross-check skips without exiftool; verify_scrub.py skips without Python and Pillow; and several filesystem assertions probe for support first, so they skip on container filesystems that do not implement what they are testing.

Exit codes, when scripting

CodeMeaningWhat to do
0Success, or nothing to do.Carry on.
1Operational error: unreadable, malformed, write failed.Read stderr — the failing path is named. Nothing was written for that file.
2--check only: identifying metadata was found.Scrub, then re-check.
64Usage error.Check the flags. -i and -d together is the usual cause.

An error outranks a dirty verdict even though 2 is the larger number, so a run that both found metadata and hit an unreadable file exits 1. That is on purpose: the unreadable file is the thing the script needs to know about first.

Filing a useful bug report

The single most useful report is the file that caused the problem. A JPEG this misreads is worth more than a description of how it misreads it.

  • Attach the file if you can. Strip it first if it is yours — scrubpony -i will do that, which is either reassuring or circular depending on the bug.
  • Include scrubpony -l output for the file, and scrubpony -n as well if the question is about policy rather than parsing.
  • Say what you expected and what happened instead.
  • Include scrubpony --version, your OS and version, and your compiler if it is a build problem.

Open an issue at the repository, or email if the file is one you would rather not put in public.

Security findings

Send them to NorseHorse@norsehor.se, encrypted with the PGP key if you prefer. The guarantees page sets out the model, including what is deliberately out of scope, so a finding against a documented limit is still worth sending — it may mean the documentation is not clear enough.

Still stuck

One developer, one inbox: NorseHorse@norsehor.se. Everything gets read.