DEV Community

Cover image for A Same-Size Old Image Passed My Upload Check. Codex Found the Race.
John
John

Posted on Originally published at hexisteme.github.io

A Same-Size Old Image Passed My Upload Check. Codex Found the Race.

Originally published on hexisteme notes.

My blog publisher draws a chart image, pastes the post into a hosted blog editor, and clicks publish on a schedule. The editor does not always keep the image I paste as is. In my probes it re-uploaded some pasted images to Kakao's CDN (blog.kakaocdn.net) and rewrote the src, left others at their original URL, and dropped an image that returned 404 from the post without an error. The re-upload is asynchronous. So before the publish click, the publisher checks the editor: the right number of images, in order, rehosted on the CDN, each with the width and height of the PNG I generated.

That last condition was the whole identity check. Codex, which I use as a second reviewer, showed that an older chart with the same dimensions satisfied it, inside a window between two steps of the publisher.

What the check compared

The publish sequence runs in this order: write the body to the clipboard, press the paste key, confirm the pasted text (PASTE_VERIFIED), wait for the rehosted images to pass, log BODY_IMAGES_VERIFIED, write an ATTEMPTED delivery marker, click publish. If the image check fails, the run stops with an error before the marker, and the next scheduled slot retries the same body.

The image check exists because the text check alone passed even when images were lost; that was a first-round Codex finding. But the image check compared metadata: the src prefix, whether rehosting had finished, the dimensions. None of those is the picture.

The second Codex round found two ways this went wrong. A missing image could report complete=true with naturalWidth=0, and the check accepted it; I fixed that by also requiring the decoded natural size. The second finding was the one this note is about. Correcting a single historical value in the chart data changed the PNG's hash but left the body text and the image dimensions identical. If an earlier run's body, carrying the earlier chart already on the CDN, landed in the paste between the clipboard write and the paste keystroke, the text check and the size check would both pass. Codex also noted that my publish-gate tests expected ok from an arbitrary CDN address and matching sizes alone, so they could not catch either defect.

The first fix did not close it

My first repair for that finding required the editor to contain zero images before the paste. That removes leftovers sitting in the editor before the run starts. It does nothing about the window after the clipboard write.

The third Codex round said so. It ran read-only and reproduced the bypass with an in-memory mock of the browser. With the editor confirmed empty, replacing the paste with the earlier body between clipboard.write and the paste key still went BODY_IMAGES_VERIFIED → ATTEMPTED → PUBLISH_CLICK. Its summary: the size comparison does not bind the image content. Five of the seven second-round findings were now blocked; the two that were not were this same defect, once in the publisher and once in the tests. The tests I had added checked a broken image and a non-empty editor before the paste, but nothing replayed the swap after the clipboard write, so the false pass had no test that would go red. It was the second round in a row to report this defect.

The recorded reproductions used mocks and did not observe a real publication. The point is that the check could not have told me if one had.

Compare what the CDN serves

The repair is compare_rehosted_images. Once the editor state looks right, the publisher downloads each rehosted image from the CDN, in order, and compares it with the source PNG. The comparison is SHA-256 first. If the bytes differ, it decodes both files and requires the same size and identical RGBA pixels. A failed download returns fetch, a mismatch returns content, and both stop the run before the ATTEMPTED marker.

I checked the assumption that makes this workable against the real service. I downloaded the CDN copy of the chart, and its SHA-256 matched the source PNG byte for byte (prefix 113b23bd). A same-size PNG with different content is blocked as content. When I reverted the fix, three of the new tests failed. The full suite finished at 1,720 passed.

The regression test that models the post-paste image state, test_rehosted_image_content_is_bound_after_paste, has two cases. A fake browser shows a rehosted image only after the clipboard write, and that image reports the expected dimensions in both cases. If the downloaded file is the fresh chart, the result contains BODY_IMAGES_VERIFIED:1. If it is a PNG with a different shade, the result is ERR:body_image_unverified(content), no publish button is clicked, and no delivery marker is written.

What a hash check still let through

The fourth Codex round, scoped to this one finding, confirmed the original bypass was blocked and the test gap was filled. It then found two new holes in the comparison itself.

  • An APNG whose first frame matches (P1). When the bytes differ, the fallback decodes the image. An animated PNG whose first frame equals the source passed the pixel comparison. The fix rejects any multi-frame file before comparing pixels.
  • Redirects that leave the CDN (P2). The download followed a redirect off the CDN and accepted a final URL outside it. The fix refuses a redirect target outside Kakao's CDN before requesting it, and checks the final URL after the response.

For both, I wrote the failing test first, then the fix. I did not run a fifth Codex round; those two fixes rest on their tests.

A side finding about the size itself

The dimensions did not even stay fixed during the work. When I injected the draft into the real blog theme on a 390px screen, the body column was 310px wide, so the 1080px PNG shrank to 0.287x and its axis labels rendered at about 8px. I moved the chart to an 800px canvas, and the smallest text at 310px became 10.9px. The first injection into the real editor recorded the rehosted chart at 1080×1670; after the canvas change, the next recorded 800×1580 with BODY_IMAGES_VERIFIED:1. Neither injection published.

Dimensions are a layout choice. In Codex's reproduction, a corrected chart kept the exact dimensions of the old one. They were never going to tell one day's chart from another's.

What this does not prove

  • A lossy platform would stop every run. The pixel fallback requires exact equality. If the platform starts re-encoding images in a way that changes the decoded RGBA pixels, each such publish will stop at content. On the day I measured, the CDN copy was byte-identical; that is one observation of one service, not a guarantee.
  • It costs a request per image. Each body image now means one CDN download, with a 15-second timeout and a 4 MB cap. A failed download stops the run before the marker, and the next slot retries.
  • The race was mocked, not seen. The fix rests on tests plus one live measurement. The first scheduled publish with this path on was the next check, not part of this record.
  • A hash binds bytes, not correctness. It shows the CDN serves the picture I generated. It does not show the chart is right, and the APNG and redirect findings show that a content check has bypasses of its own to enumerate.

The lesson I took: equal dimensions are not identity. When a third party rehosts your asset, download what it serves and compare content hashes, then list what the comparison still accepts. It is the same rule as verifying the output surface: tests verify the payload you build, not what the other system renders or, here, serves.

Email list for these notes: hexisteme.beehiiv.com — no issue has gone out yet, so you would be on it before the first one. No welcome sequence, no course, no upsell.

More notes at hexisteme.github.io/notes.

Top comments (0)