Skip to content

mobile: mixed image+file share skips the image size gate before base64 #229

Description

@rynfar

Found during adversarial review of #8237 adoption. Upstream code, adopted as-is.

Problem

apps/mobile/src/features/sharing/IncomingShareProvider.tsx:157-163:

const hasGenericFilePayload = payloads.some((p) => ["file","audio","video"].includes(p.shareType));
const resolvedPayloads =
  !hasGenericFilePayload && payloads.some((p) => p.shareType === "image")
    ? await resolvedPayloadsForFiles()
    : [];

The image branch's only size check before reading bytes is gated on that resolution (incoming-share-model.ts:396-403). With resolved === undefined the check is skipped and control reaches :409 await input.fileReader.readBase64(uri) -> new File(uri).base64(), uncapped.

Trigger

Newly reachable because the same changeset widened registration: app.config.ts now uses singleShareMimeTypes: ["*/*"] / multipleShareMimeTypes: ["*/*"] on Android and supportsFileWithMaxCount: 8 on iOS.

Share one PDF + one large photo together from a file manager. Also fires on any image share where getResolvedSharedPayloadsAsync() throws, since the catch at :63-70 also returns [].

Consequence

A 300 MB image is materialised as a ~400 MB base64 string before the 10 MB check that would have rejected it.

Amplifier

incoming-share-inbox.ts:129 calls clearNativePayloads() only after buildDraft and writeDraft succeed, and iOS payloads persist in the App Group UserDefaults. If OOM kills the process rather than throwing a catchable error, refresh() re-ingests the identical payload on next launch — a launch loop with no in-app escape.

Related

The image branch trusts resolved.contentSize, which on Android is the sender's OpenableColumns.SIZE, not a measurement. The file branch was explicitly hardened against exactly this (incoming-share-model.ts:339-358 measures the persisted copy, test at incoming-share-model.test.ts:245). The image branch was not.

Sketch

Measure before reading, or gate the base64 read on a stat regardless of which branch resolved. The !hasGenericFilePayload && clause has no comment or test explaining its rationale — worth recovering that before changing it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions