bingo: strip photo metadata, identify to Wikimedia
Photos are served byte-for-byte, and the Commons shot of Medvedkovo carries GPS coordinates in its EXIF, while the card is meant to stay city-level. Drop Exif/XMP/IPTC/comment segments from JPEG, text/eXIf chunks from PNG and EXIF/XMP chunks from WebP on store; pixel data is left alone and malformed files are rejected. upload.wikimedia.org answered 429 to the generic agent; its policy asks for a tool name and contact, so send one. Add a "re-import all" admin action so photos stored before this change get re-fetched and stripped. Assisted-by: Claude:opus-5
This commit is contained in:
parent
7820f138f6
commit
903dc17a75
4 changed files with 105 additions and 4 deletions
|
|
@ -3,7 +3,8 @@ require_relative 'test_helper'
|
|||
class BingoTest < Minitest::Test
|
||||
include Rack::Test::Methods
|
||||
|
||||
JPEG = "\xFF\xD8\xFF\xE0fakejpeg".b
|
||||
# Minimal well-formed JPEG: SOI, start-of-scan, data, EOI.
|
||||
JPEG = ("\xFF\xD8\xFF\xDA".b + [8].pack('n') + "\1\2\3\4\5\6data\xFF\xD9".b).freeze
|
||||
|
||||
def app
|
||||
@app || BingoWeb
|
||||
|
|
@ -84,6 +85,34 @@ class BingoTest < Minitest::Test
|
|||
S3.instance_variable_set(:@client, nil)
|
||||
end
|
||||
|
||||
def test_strips_jpeg_exif
|
||||
exif = "\xFF\xE1".b + [17].pack('n') + "Exif\0\0GPSDATA!!".b
|
||||
jfif = "\xFF\xE0".b + [16].pack('n') + "JFIF\0".b + ("\0" * 9).b
|
||||
scan = "\xFF\xDA".b + [8].pack('n') + "\1\2\3\4\5\6pixels\xFF\xD9".b
|
||||
out = Bingo::Photo.strip_metadata("\xFF\xD8".b + jfif + exif + scan, 'jpg')
|
||||
refute_includes out, 'GPSDATA'
|
||||
assert_equal "\xFF\xD8".b + jfif + scan, out
|
||||
end
|
||||
|
||||
def test_strips_png_text_chunks
|
||||
chunk = ->(type, data) { [data.bytesize].pack('N') + type + data + "\0\0\0\0".b }
|
||||
sig = "\x89PNG\r\n\x1A\n".b
|
||||
ihdr = chunk.('IHDR', "\0" * 13)
|
||||
out = Bingo::Photo.strip_metadata(sig + ihdr + chunk.('eXIf', 'GPS') + chunk.('tEXt', 'k\0v') + chunk.('IEND', ''), 'png')
|
||||
assert_equal sig + ihdr + chunk.('IEND', ''), out
|
||||
end
|
||||
|
||||
def test_strips_webp_exif
|
||||
vp8x = 'VP8X'.b + [10].pack('V') + [0x08 | 0x10].pack('C') + ("\0" * 9).b # EXIF + alpha flags
|
||||
img = 'VP8L'.b + [5].pack('V') + "abcde\0".b
|
||||
exif = 'EXIF'.b + [3].pack('V') + "GPS\0".b
|
||||
body = vp8x + img + exif
|
||||
out = Bingo::Photo.strip_metadata('RIFF'.b + [body.bytesize + 4].pack('V') + 'WEBPh'.b[0, 4] + body, 'webp')
|
||||
refute_includes out, 'GPS'
|
||||
assert_equal 0x10, out.getbyte(20) # alpha kept, EXIF flag cleared
|
||||
assert_equal out.bytesize - 8, out.byteslice(4, 4).unpack1('V')
|
||||
end
|
||||
|
||||
def test_photo_rejects_non_images
|
||||
assert_raises(Bingo::Photo::Error) { Bingo::Photo.store('dc-1', '<html>') }
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue