majorotherverifiedfirsthand
A receipt pipeline OCR'd screenshots and wrote ¥73,158 of fake spending into the ledger
An automated pipeline that OCRs receipt photos into a household ledger also swallowed screenshots and browser-saved images, writing 5 fake expenses totaling ¥73,158 into the ledger.
Cause
Four holes lined up. Every image in the Downloads folder was picked up unconditionally. OCR returned an amount whenever it saw a number, and that amount went into the ledger unchecked. The Excel summary showed stale cached results. And duplicate detection went by filename only, so the same image arriving under a different name was recorded twice.
Consequence
Five nonexistent expenses landed in the household ledger. The largest, ¥66,000, came from a quote screenshot from one of the operator's own dashboards. The fix commit then sat unpushed for about two months.
Fix
Only accept images that actually arrived via AirDrop, require camera EXIF data to prove it is a photo, force a full recalculation on load, and deduplicate on the image's SHA-256.
What happened
Snap a receipt on an iPhone, AirDrop it to the Mac, and it lands in Downloads.
A launchd job moves it to a working folder, OCRs it, appends it to a ledger
CSV, refreshes an Excel summary, and posts a notification. A fully automatic
receipt pipeline.
The investigation started with a vague “I don’t think this is working right.”
It turned out launchd was running perfectly. It was running perfectly and
importing the wrong things. The ledger held five expenses that never happened,
totaling ¥73,158.
The chaos on the ground
The biggest one was ¥66,000. Its source was a quote screenshot from one of the operator’s own dashboards, showing a total of ¥66,000, tax included. Trying to filter on receipt-like words didn’t help: the screenshot used exactly the same words a real receipt does. Keyword checks alone couldn’t tell them apart.
It wasn’t one hole, either:
- The mover script carried every image in Downloads, whatever its origin. Screenshots and browser-saved images were all treated as receipts.
- OCR returns an amount whenever an image contains a number, and that amount went into the ledger without any check.
- The step that updated Excel from the ledger rewrote cell values only. Excel trusts its cached formula results, so the summary kept showing old numbers.
- Duplicates were detected by filename, so the same image arriving twice under different names was recorded twice.
The fix was committed the same day. That commit then lived only on the local machine for about two months, until it was finally pushed alongside an unrelated fix.
Root cause
Nobody questioned the inputs. “An image in Downloads is a receipt” and “if OCR returned an amount, it’s a receipt” were two unchecked assumptions chained together. Automation that doesn’t doubt what comes in will carry garbage into the books with perfect accuracy.
The fix
- AirDrop check: Measured on real files, AirDropped images carry a
com.apple.quarantineattribute whose agent issharingd; browser downloads carry a different value, and screenshots have no such attribute at all. That narrowed the front door. - Camera-photo check: Keyword checks lost to the quote screenshot. The
deciding signal was the EXIF camera make: across 13 real samples, only the
genuine receipt photos had
Make=Apple, and everything else failed. - Forced recalculation: The workbook now carries
fullCalcOnLoad="1"and its calculation chain is removed. The flag is confirmed to be in the file; recalculation has not been verified by actually opening it in Excel. - Content-based dedup: The ledger gained an
image_sha256column, so duplicates are judged by content rather than filename.
The ledger now held 51 entries, and the test suite grew from 74 to 96.
Sources
- Firsthand account from the site operator. There is no public write-up to link to.