contact: warn on a PHOTO of no known image format
The sniff's nil was the last silent failure: a format the signature
list misses, or junk that happened to decode, showed as initials with
no trace.
Assisted-by: GLM-5.3 via pi
diff --git a/lib/pro_tacts/contact.rb b/lib/pro_tacts/contact.rb
index f48d513..aff457d 100644
--- a/lib/pro_tacts/contact.rb
+++ b/lib/pro_tacts/contact.rb
@@ -162,11 +162,14 @@ module ProTacts
# PHOTO, an undecodable one, or one that sniffs to no known format
# — the initials an avatar falls back to, an ordinary absence
# rather than an error, exactly like the other accessors' nils.
- # The undecodable nil is the one departure, and reports on its way
- # there — warning-grade, carrying no card content — because a
- # PHOTO this server cannot read is news, the URI form included:
- # legal vCard (RFC 2426 section 3.1.4) that no client here sends
- # yet. At the read rather than the arrival, unlike the parser's
+ # The two failures among those nils report on the way there —
+ # warning-grade, carrying no card content — because a PHOTO this
+ # server cannot read is news either way it fails: a decode
+ # refusal could be the URI form, legal vCard (RFC 2426 section
+ # 3.1.4) that no client here sends yet, and a decode that sniffs
+ # to nothing is either junk that happened to decode or an image
+ # format the signature list does not cover yet. At the read
+ # rather than the arrival, unlike the parser's
# reports (Web#report_broken_assumptions): no arrival probe reads
# PHOTO, and a stored card can predate the report, while the
# decode runs only where an avatar renders and Sentry groups the
@@ -184,7 +187,13 @@ module ProTacts
end
signature = PHOTO_SIGNATURES.find { bytes.start_with?(it.first) }
- return if signature.nil?
+ if signature.nil?
+ Sentry.capture_message(
+ "a card's PHOTO decoded to no image format this server recognizes",
+ level: :warning,
+ )
+ return
+ end
Photo.new(mime_type: signature.fetch(1), bytes:)
end
diff --git a/test/pro_tacts/test_contact.rb b/test/pro_tacts/test_contact.rb
index abeb54e..9e355c8 100644
--- a/test/pro_tacts/test_contact.rb
+++ b/test/pro_tacts/test_contact.rb
@@ -257,6 +257,9 @@ class ContactTest < Minitest::Test
def test_a_card_without_a_photo_has_none
assert_nil contact.photo
+
+ # The one nil that stays quiet: no PHOTO is no news.
+ assert_empty sentry_events
end
# A URI-form PHOTO never reaches the sniff: a URI's ":" is not in
@@ -288,14 +291,17 @@ class ContactTest < Minitest::Test
assert_equal "ArgumentError", event.exception.values.first.type
end
- # The other nils stay quiet: an image that decodes but sniffs to no
- # known format is an allowlist gap with no exception to name, an
- # ordinary absence exactly like no PHOTO at all.
- def test_a_photo_of_no_known_format_stays_quiet
+ # The sniff's nil reports too, a message rather than an exception —
+ # there is no error to name, only bytes whose format the signature
+ # list does not know, and the message is the record that they
+ # arrived.
+ def test_a_photo_of_no_known_format_warns
unknown = CARD.sub("FN:Aiden\r\n", "PHOTO;ENCODING=b:#{Base64.strict_encode64("RIFF____WEBP")}\r\n")
assert_nil contact(unknown).photo
- assert_empty sentry_events
+
+ assert_equal 1, sentry_messages.length
+ assert_match(/PHOTO decoded to no image format/, sentry_messages.fetch(0))
end
# One PHOTO property carrying a PNG payload, for the sniff's rows.