Skip to content

fix(mobile): Android image metadata stripping fixes#2188

Merged
brow merged 3 commits into
mainfrom
bot-1389-android-media-sanitizer
Jul 21, 2026
Merged

fix(mobile): Android image metadata stripping fixes#2188
brow merged 3 commits into
mainfrom
bot-1389-android-media-sanitizer

Conversation

@brow

@brow brow commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

What changed?

Android image uploads now decode into sRGB before encoding, preserve PNG transparency, and structurally remove PNG and JPEG metadata that the relay rejects. Android 7.x retains a compatible decode path without raising the minimum supported API.

Real API 36 Bitmap.compress fixtures and relay contract tests cover sRGB and Display P3 PNG/JPEG output.

Why?

Android image uploads can fail with HTTP 422 when platform encoding emits ICC profiles or other non-canonical metadata. The client must meet the relay contract without weakening the relay's metadata policy.

How is it tested?

Added tests:

Validated on API 24 and API 36, including Display P3 conversion and transparent PNG pixel preservation. Full pre-push mobile, Rust, desktop, and Tauri suites passed. Android lint has no new API findings and remains blocked by two existing unrelated errors.

Related: BOT-1389

Screenshots are not practical for this non-visual media-encoding change. Coverage verifies output color space, pixels, alpha, metadata structure, and relay acceptance instead.

🤖 This PR was authored with an agent.

Co-authored-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>
@brow

brow commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@brow

brow commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@builderbot review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

Comment thread mobile/android/app/src/main/kotlin/xyz/block/buzz/mobile/AndroidMediaSanitizer.kt Outdated
Co-authored-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>
@brow
brow marked this pull request as ready for review July 21, 2026 01:29
@brow
brow requested a review from a team as a code owner July 21, 2026 01:29
@brow
brow enabled auto-merge (squash) July 21, 2026 01:29
@brow brow changed the title fix(mobile): sanitize Android image uploads fix(mobile): Android image metadata stripping fixes Jul 21, 2026

@brow brow left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Re-reviewed exact head 77ea4bb. The unsigned SOI comparison and malformed double-SOI regression now enforce the relay contract correctly. I found no blocking issues. One non-blocking fault-visibility concern is noted inline.

Comment thread mobile/android/app/src/main/kotlin/xyz/block/buzz/mobile/MainActivity.kt Outdated
Co-authored-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>

@brow brow left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Independent review at exact head 7d8bd73 (second reviewer in the dual-review loop; delta from 77ea4bb is the narrowed upload-boundary catch).

Verified:

  • Narrowing delta is correct and complete: runCatching {}.getOrNull()try/catch (IllegalArgumentException). The sanitizer's fail-closed rejections (require) map to the normal upload-failure path, while error()'s IllegalStateException (unreachable format invariant) and OutOfMemoryError now propagate instead of masquerading as upload failures. This resolves the fault-visibility concern without widening behavior: a malformed Bitmap.compress output still never uploads unsanitized.
  • PNG scrub: strict allowlist; drops eXIf/iCCP/tEXt/iTXt/zTXt/tIME/pHYs while keeping color-neutral and APNG chunks; length fields validated via 64-bit arithmetic (no overflow on hostile lengths); truncated chunks and missing IEND fail closed; trailing bytes after IEND dropped (tested with appended payload).
  • JPEG scrub: keeps only zero-thumbnail-consistent JFIF APP0 and exact 12-byte Adobe APP14; drops APP1–APP13/APP15/COM (EXIF, XMP, ICC); entropy-coded data with byte stuffing and restart markers handled; nested SOI rejected using the unsigned comparison (77ea4bb fix) with the exact malformed double-SOI regression present; missing EOI fails closed; trailing bytes dropped.
  • sRGB-before-encode: decodeSrgbBitmap targets sRGB at decode (ImageDecoder ≥P with software allocator, inPreferredColorSpace on 26–27) and re-draws to an ARGB_8888 sRGB bitmap otherwise, so dropping iCCP/APP2 cannot ship mis-tagged Display-P3 pixels; pre-O passes through where no color management exists. Instrumented test pins the P3→sRGB conversion within tolerance for both formats.
  • Fixture integrity: all Bitmap.compress fixtures are byte-identical across crates/buzz-media, JVM-test, and androidTest resources (cmp clean); canonical sRGB PNG is a scrub fixed point (sanitized == input); regeneration procedure documented against API 36.
  • Relay contract closes the loop: cargo test -p buzz-media android_ passes locally at this head — all four sanitized fixtures accepted, all three metadata-bearing raw encoder outputs rejected with MetadataForbidden. These Rust tests run in the CI Unit Tests job (rust path filter covers crates/**).
  • CI: fully green at 7d8bd73 (30 success / 2 skipped publish jobs), including Mobile and Unit Tests.

Two non-blocking inline notes below (CI wiring for the new Kotlin JVM tests; pre-existing broad catch on the decode fallback). Verdict: approve.

}
}

dependencies {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Non-blocking: the new JVM sanitizer tests (app:testDebugUnitTest) are not executed by the CI mobile job, which runs flutter test and flutter build apk only (assembleDebug does not run unit tests). The relay-side contract tests in crates/buzz-media do run in CI and pin the actual byte contract, so this is coverage redundancy rather than a gap — but wiring testDebugUnitTest into the mobile CI job in a follow-up would keep the structural scrubber tests from ever going stale unnoticed.

@RequiresApi(Build.VERSION_CODES.O)
private fun decodeColorManagedBitmap(bytes: ByteArray): Bitmap? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
runCatching {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Non-blocking, pre-existing pattern: this decode-path runCatching still catches every Throwable (including OOM), in contrast to the now-narrowed encode path. It guards a genuine documented fallback (ImageDecoder → BitmapFactory handles real decode failures), so converting decode faults into the BitmapFactory attempt is defensible; flagging only so the asymmetry with the encode path is a recorded choice rather than an accident.

@brow
brow merged commit ee21da9 into main Jul 21, 2026
32 checks passed
@brow
brow deleted the bot-1389-android-media-sanitizer branch July 21, 2026 02:18
brow pushed a commit that referenced this pull request Jul 21, 2026
…obile-releasing

* origin/main:
  chore(release): release Buzz Mobile version 0.4.9 (#2200)
  Simplify first-community onboarding choices (UI only) (#2194)
  fix(mobile): sanitize Android image uploads (#2188)

Co-authored-by: npub1sv749mw8zcmld4ygjx2mx2aqcn3zvtuj2nlmgatxgad3t9uweu9q5marze <833d52edc71637f6d4889195b32ba0c4e2262f9254ffb47566475b15978ecf0a@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub1sv749mw8zcmld4ygjx2mx2aqcn3zvtuj2nlmgatxgad3t9uweu9q5marze <833d52edc71637f6d4889195b32ba0c4e2262f9254ffb47566475b15978ecf0a@sprout-oss.stage.blox.sqprod.co>

# Conflicts:
#	mobile/pubspec.yaml
brow pushed a commit that referenced this pull request Jul 21, 2026
* origin/main:
  chore(release): release Buzz Mobile version 0.4.9 (#2200)
  Simplify first-community onboarding choices (UI only) (#2194)
  fix(mobile): sanitize Android image uploads (#2188)
brow added a commit that referenced this pull request Jul 21, 2026
* origin/main:
  chore(release): release Buzz Mobile version 0.4.9 (#2200)
  Simplify first-community onboarding choices (UI only) (#2194)
  fix(mobile): sanitize Android image uploads (#2188)

Co-authored-by: Tom Brow <tomb@block.xyz>
Signed-off-by: Tom Brow <tomb@block.xyz>
wpfleger96 pushed a commit that referenced this pull request Jul 21, 2026
…chive

* origin/main: (25 commits)
  feat(cli): filter archived instances from --template roster resolution (#2207)
  chore(release): release Buzz Desktop version 0.4.21 (#2209)
  fix(desktop): clarify data deletion action (#2208)
  feat: brand authentication complete page (#2192)
  fix(buzz-acp,buzz-agent): surface stall duration and fate (#2204)
  fix(desktop): resolve activity feed showing channel UUID instead of message content (#2201)
  feat(cli): add agents archive/unarchive/archived subcommands (#2173)
  chore(acp): strip stale finding-number references from comments (#2202)
  chore(release): release Buzz Mobile version 0.4.9 (#2200)
  Simplify first-community onboarding choices (UI only) (#2194)
  fix(mobile): sanitize Android image uploads (#2188)
  fix(cli): paginate channel directory queries (#2181)
  Fix persisted agent defaults display (#2182)
  [codex] Fix missing release tag detection (#2191)
  feat(web): authenticate gated community browsing (#2190)
  fix(timeout): unified turn-timeout fix — cap inheritance, steer renewal, activity-aware requeue, LLM stall surfacing (#2175)
  fix(desktop): scope draft store per workspace relay (#2179)
  chore(release): release Buzz Mobile version 0.4.8 (#2187)
  [codex] create release tags with dedicated App (#2186)
  fix(desktop): avoid forwarding click event as channel callback (#2174)
  ...
wpfleger96 added a commit that referenced this pull request Jul 21, 2026
…chive

* origin/main: (25 commits)
  feat(cli): filter archived instances from --template roster resolution (#2207)
  chore(release): release Buzz Desktop version 0.4.21 (#2209)
  fix(desktop): clarify data deletion action (#2208)
  feat: brand authentication complete page (#2192)
  fix(buzz-acp,buzz-agent): surface stall duration and fate (#2204)
  fix(desktop): resolve activity feed showing channel UUID instead of message content (#2201)
  feat(cli): add agents archive/unarchive/archived subcommands (#2173)
  chore(acp): strip stale finding-number references from comments (#2202)
  chore(release): release Buzz Mobile version 0.4.9 (#2200)
  Simplify first-community onboarding choices (UI only) (#2194)
  fix(mobile): sanitize Android image uploads (#2188)
  fix(cli): paginate channel directory queries (#2181)
  Fix persisted agent defaults display (#2182)
  [codex] Fix missing release tag detection (#2191)
  feat(web): authenticate gated community browsing (#2190)
  fix(timeout): unified turn-timeout fix — cap inheritance, steer renewal, activity-aware requeue, LLM stall surfacing (#2175)
  fix(desktop): scope draft store per workspace relay (#2179)
  chore(release): release Buzz Mobile version 0.4.8 (#2187)
  [codex] create release tags with dedicated App (#2186)
  fix(desktop): avoid forwarding click event as channel callback (#2174)
  ...

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Jul 21, 2026
…chive

* origin/main: (25 commits)
  feat(cli): filter archived instances from --template roster resolution (#2207)
  chore(release): release Buzz Desktop version 0.4.21 (#2209)
  fix(desktop): clarify data deletion action (#2208)
  feat: brand authentication complete page (#2192)
  fix(buzz-acp,buzz-agent): surface stall duration and fate (#2204)
  fix(desktop): resolve activity feed showing channel UUID instead of message content (#2201)
  feat(cli): add agents archive/unarchive/archived subcommands (#2173)
  chore(acp): strip stale finding-number references from comments (#2202)
  chore(release): release Buzz Mobile version 0.4.9 (#2200)
  Simplify first-community onboarding choices (UI only) (#2194)
  fix(mobile): sanitize Android image uploads (#2188)
  fix(cli): paginate channel directory queries (#2181)
  Fix persisted agent defaults display (#2182)
  [codex] Fix missing release tag detection (#2191)
  feat(web): authenticate gated community browsing (#2190)
  fix(timeout): unified turn-timeout fix — cap inheritance, steer renewal, activity-aware requeue, LLM stall surfacing (#2175)
  fix(desktop): scope draft store per workspace relay (#2179)
  chore(release): release Buzz Mobile version 0.4.8 (#2187)
  [codex] create release tags with dedicated App (#2186)
  fix(desktop): avoid forwarding click event as channel callback (#2174)
  ...

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
tlongwell-block pushed a commit that referenced this pull request Jul 21, 2026
* origin/main:
  fix(desktop): keep project branches after reload (#2214)
  fix(git): make project branch workflows reliable (#2213)
  feat(desktop): manage project branches (#2212)
  fix(acp): honor relay rate limits and pace resubscribes on bad links (#2199)
  chore(lefthook): add CI-style path filtering to hooks (#2211)
  feat(cli): manage repository protection rules (#2193)
  feat(cli): filter archived instances from --template roster resolution (#2207)
  chore(release): release Buzz Desktop version 0.4.21 (#2209)
  fix(desktop): clarify data deletion action (#2208)
  feat: brand authentication complete page (#2192)
  fix(buzz-acp,buzz-agent): surface stall duration and fate (#2204)
  fix(desktop): resolve activity feed showing channel UUID instead of message content (#2201)
  feat(cli): add agents archive/unarchive/archived subcommands (#2173)
  chore(acp): strip stale finding-number references from comments (#2202)
  chore(release): release Buzz Mobile version 0.4.9 (#2200)
  Simplify first-community onboarding choices (UI only) (#2194)
  fix(mobile): sanitize Android image uploads (#2188)
  fix(cli): paginate channel directory queries (#2181)

Semantic resolutions in crates/buzz-acp/src/lib.rs:
- kept lazy_pool startup + pool_ready select guard from this branch,
  adopted main's comment cleanup (#2202)
- dropped this branch's duplicated steer_renewal_tests module: #2204 moved
  those tests to queue.rs on main

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants