fix(gateway): hash full image data URL for cache key to prevent collision

Root cause: makeImageCacheKey() only hashed the first 128 chars of the
data URL. Since all resized images use the same MIME prefix
('data:image/png;base64,') + identical base64 header bytes, nearly every
image got the same 16-char hash → 'image:<same-hash>' → all images reused
the first cached vision analysis (often a gambling-detection verdict).

Fix: hash the entire data URL instead of just the prefix. Verified
114 stale 'image:' entries + 745 stale 'phash:' entries purged from prod
DB. tsc --noEmit clean, 133 tests pass.
This commit is contained in:
asepharyana
2026-08-12 18:28:19 +07:00
parent bd292fdf3d
commit 9f7ce7dbd5
@@ -63,12 +63,15 @@ export function makeCustomEmojiCacheKey(emojiId: string): string {
/**
* Generate a deterministic cache key for an image data URL.
* Hashes the first 128 chars of the data URL (enough to identify the image
* without storing the full base64 string as the key).
* Hashes the FULL data URL — only hashing a prefix (e.g. first 128 chars)
* causes hash collisions for images that share the same MIME prefix +
* identical base64 header bytes (common when images are resized to the same
* dimensions), which makes every image incorrectly reuse the same cached
* vision analysis. Hashing the entire data URL guarantees uniqueness per
* actual pixel content.
*/
export function makeImageCacheKey(dataUrl: string): string {
const prefix = dataUrl.slice(0, 128);
const hash = createHash("sha256").update(prefix).digest("hex").slice(0, 16);
const hash = createHash("sha256").update(dataUrl).digest("hex").slice(0, 16);
return `image:${hash}`;
}