Skip to content

docs: state the invariant that makes the media field decode safe - #633

Draft
jkmassel wants to merge 1 commit into
fix/media-uploader-credentials-trapfrom
docs/media-field-decode-invariant
Draft

docs: state the invariant that makes the media field decode safe#633
jkmassel wants to merge 1 commit into
fix/media-uploader-credentials-trapfrom
docs/media-field-decode-invariant

Conversation

@jkmassel

@jkmassel jkmassel commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Stacked on #632. Last of ten PRs splitting #621.

What?

Documents why formFields' UTF-8 decode is lossless, and covers the partition that makes it so.

Why?

formFields decodes each non-file form value as UTF-8, which substitutes U+FFFD on malformed input. That is lossless today, but only because of an invariant nothing in the code states or enforces: the sole client is the editor's browser FormData.

  • The server binds to loopback behind a per-session token, so nothing else can reach it.
  • A FormData string value is a USVString, already well-formed at append time.
  • Its only way to carry arbitrary bytes is a Blob, which always gets a filename and is filtered out of extraParts.

Worth writing down because if it stops holding, the two platforms are lossy differently — so there is no single behavior that could be documented instead. Relaxing each platform's filename filter demonstrates it: for ED A0 80, Swift yields three replacement characters under its maximal-subpart rule where Java's decoder yields one. Both observed, not inferred.

How?

  • The invariant on formFields, both platforms.
  • Reworded the raw-bytes comment on the re-encode path. "So a non-UTF-8 value is forwarded verbatim" read as though malformed values were expected, which made the two delivery paths look contradictory. The actual hazard is the failable decode returning nil and an obvious ?? "" dropping the whole value; the reason to keep bytes is that the re-encode should stay byte-identical to the passthrough it stands in for.
  • Restores attachmentId's doc comment, which feat: add MediaUploader, for a host that owns the whole upload #628 orphaned onto formFields when it inserted that helper above it.

Testing Instructions

Cover the partition rather than the decode, since the partition is what makes the invariant true: a request carrying a second, Blob-shaped part whose bytes are not valid UTF-8 must not surface that part in fields. A second test pins the other half — valid UTF-8 (emoji, non-Latin scripts) round-trips exactly.

  • Both fail when the filename filter is relaxed, so neither is vacuous
  • swift test — host suite green
  • Android :Gutenberg:testDebugUnitTest green
  • SwiftLint + Detekt clean

Neither test asserts what becomes of that second part — it is currently dropped rather than relayed, which is a separate open question.

`formFields` decodes each non-file form value as UTF-8, which substitutes
U+FFFD on malformed input. That is lossless today, but only because of an
invariant nothing in the code states or enforces: the sole client is the
editor's browser FormData. The server binds to loopback behind a
per-session token; a FormData string value is a USVString, already
well-formed at append time; and its only way to carry arbitrary bytes is a
Blob, which always gets a filename and is filtered out of `extraParts`.

Write that down on both platforms, including the part that makes it
matter — if it stops holding, the two platforms are lossy *differently*,
so there is no single behavior that could be documented instead. Relaxing
each platform's filename filter demonstrates it: for `ED A0 80` Swift
yields three replacement characters under its maximal-subpart rule where
Java's decoder yields one.

Cover the partition rather than the decode, since the partition is what
makes the invariant true: a request carrying a second, Blob-shaped part
whose bytes are not valid UTF-8 must not surface that part in `fields`. A
second test pins the other half — valid UTF-8 (emoji, non-Latin scripts)
round-trips exactly, so real captions are unaffected. Both fail when the
filter is relaxed, so neither is vacuous. Neither asserts what becomes of
that second part — it is currently dropped rather than relayed, which is a
separate open question.

Also reword the raw-bytes comment on the re-encode path. "So a non-UTF-8
value is forwarded verbatim" read as though malformed values were
expected, which made the two delivery paths look contradictory. The actual
hazard is the failable decode returning nil and an obvious `?? ""`
dropping the whole value; the reason to keep bytes is that the re-encode
should stay byte-identical to the passthrough it stands in for.

Finally, restore `attachmentId`'s doc comment, which an earlier commit in
this series orphaned onto `formFields` when it inserted the helper above
it.
@jkmassel jkmassel self-assigned this Sep 5, 2026
@jkmassel jkmassel added the iOS label Sep 5, 2026
@wpmobilebot

Copy link
Copy Markdown

XCFramework Build

This PR's XCFramework is available for testing. Add the following to your Package.swift:

.package(url: "https://github.com/wordpress-mobile/GutenbergKit", branch: "pr-build/633")

Built from 3b6ad45

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants