[iOS / HTTP Upload] Original filenames from Files are exposed to the XMPP server and S3 storage (XEP-0363) #16
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
[iOS / HTTP Upload] Original filenames from Files are exposed to the XMPP server and S3 storage (XEP-0363)
Severity: Major
Issue ID: UP-02 · QA case: MD-FL-01
Date: October 26, 2026 · Updated November 4, 2026
Summary
When a user sends an attachment selected from the iOS Files app, AnotherIM includes the original filename in the XEP-0363 HTTP Upload slot request.
The filename is exposed to the XMPP server and embedded in the S3 object URL, even when the file content is protected by OMEMO encryption.
For example, uploading
IMG_4821.HEICproduces:filename='IMG_4821.HEIC'./IMG_4821.HEIC.This can disclose sensitive metadata, such as document names, to infrastructure operators. In chats without OMEMO, the attachment URL is also visible in plaintext to the XMPP server and potentially stored in MAM.
Affected components:
Packages/FeatureConversation/Sources/ConversationUI/ConversationView.swiftAnotherIM/XMPP/Attachments/AttachmentService.swiftAnotherIM/XMPP/Types/Attachments.swifttacky/xmpp/modules/message.tcl,file.tclThe issue affects files selected from Files and drag-and-drop attachments based on the build 93 source code.
Gallery photos/videos and voice recordings use generated filenames. Camera video filename behavior has not been verified.
Environment
c5fdeae, branchfeat/ui-redesign, build 932f42a2f2/ v1.2.0-beta2mod_s3_uploadfrom anotherim-serverb9e675dautotests/metadata-auditThe test server generates signed upload URLs locally; no actual S3 upload was performed.
The iOS application path was verified through source code, not by executing build 93 on a physical device.
Preconditions
Start the test environment:
PUSH_WINDOW=0 GRACE_PERIOD=0 CLEAN=1 bash setup/up.shBuild libtacky from the application submodule:
TACKY_SRC=<anotherim-swift/tacky> bash setup/build_tacky.shConfigure
main@example.localwith the upload serviceupload.example.local.Steps to Reproduce
Automated test
Run:
cd checks && python3 tacky_more.pyThe test requests an HTTP Upload slot for
IMG_4821.HEIC(2,483,117 bytes,image/heic) usingmessage upload_slot.Inspect the XMPP stanza log for
urn:xmpp:http:upload:0.Check the
filenameattribute and returned GET/PUT URLs.Manual verification on iOS (not yet performed)
Contract_Test.pdf, and send it.upload_slot, or capture the server-side XMPP upload request.The application log is available only in DEBUG builds. Ordinary Charles interception is insufficient for inspecting the upload PUT because the libtacky iOS HTTP client uses bundled trusted root certificates.
Expected Behavior
The HTTP Upload slot request should use a generated filename, such as
<UUID>.pdf, instead of the original local filename.The original filename should remain available only inside the encrypted message payload when OMEMO is enabled.
This follows QA case MD-FL-01, which expects a randomized filename and
application/octet-streamcontent type in the slot request.Note: This is a security/privacy recommendation, not an explicitly documented product requirement. Whether to conceal the MIME type requires a separate decision.
Actual Behavior
Run
fd861f91(evidence/stanzas_upload_fd861f91.txt) captured the following request:The server returned URLs containing the same filename:
The slot request size includes the additional 16-byte GCM authentication tag used for OMEMO attachment encryption.
This confirms that the original filename reaches the XMPP upload service and is incorporated into the storage URL.
The filename may be visible to:
The attachment content remains encrypted when OMEMO is used, but the filename is not protected in the upload request.
Reproduction Rate
6/6 successful automated runs:
4f096705,53e2a4089916fa6b,fb719fdbfd861f91,31fa65faBoth November 3 runs reproduced the issue with libtacky v1.2.0-beta2 and the build 93 source revision.
Root Cause
The iOS attachment pipeline preserves the original filename and passes it unchanged to the HTTP Upload slot request:
ConversationView.swift:379createsOutgoingFilewithname: url.lastPathComponent.ConversationClient+Live.swift:115,124forwards the filename throughPickedAttachment.file.Attachments.swift:143-147sanitizes control characters and path separators but does not anonymize the filename.AttachmentService.swift:507passespicked.nametoapi.messageUploadSlot.TackyAPI.swift:556forwards the request to libtacky.message.tcl:5185-5205andfile.tcl:2354-2358insert the supplied filename into the XEP-0363<request/>stanza.The temporary local copy uses a UUID directory, but the original filename is retained.
Other attachment sources (source-code assessment):
photo-<timestamp>.<ext>/video-<timestamp>.<ext>.voice-<UUID>.m4a.photo-<UUID>.jpg.Suggested Fix
Generate a random upload filename centrally in
AttachmentService.performUploadbefore callingmessageUploadSlot.For example:
<UUID>.<extension>or<UUID>.Applying this at the upload-service level would cover Files, drag-and-drop, and other attachment sources consistently.
Separately evaluate whether the upload request should use
application/octet-streamrather than the original MIME type. The current MIME type is intentionally preserved to support attachment previews in clients such as Gajim.Verification After Fix
Contract_Test.pdffrom Files.Contract_Test.pdf.Evidence and Status
Evidence directory:
2026-10-26_UP-02_upload_filename/evidence/November 3 evidence:
stanzas_upload_fd861f91.txtstanzas_upload_31fa65fa.txtstanzas_full_fd861f91.logstanzas_full_31fa65fa.logtacky_more_fd861f91.jsontacky_more_31fa65fa.jsonTest report:
autotests/metadata-audit/results/2026-11-03.mdComplete evidence archive:
_claude_tmp/evidence_ma_2026-11-03.tgzVerified:
Not verified:
4832765: the upload slot is requested under .; the real name goes only in the message. The MIME type is unchanged.