Skip to content

fix(studio-server): preserve binary bodies in file PUT/POST - #1914

Closed
yegor177 wants to merge 1 commit into
heygen-com:mainfrom
yegor177:fix/studio-files-binary-upload
Closed

fix(studio-server): preserve binary bodies in file PUT/POST#1914
yegor177 wants to merge 1 commit into
heygen-com:mainfrom
yegor177:fix/studio-files-binary-upload

Conversation

@yegor177

@yegor177 yegor177 commented Jul 3, 2026

Copy link
Copy Markdown

Problem

PUT/POST /api/projects/:id/files/* in @hyperframes/studio-server read the request body with c.req.text() and write it with writeFileSync(path, body, "utf-8"). Any body that is not valid UTF-8 — fonts, images, any binary asset — gets its non-UTF-8 bytes replaced with U+FFFD during the decode, so the file on disk is silently corrupted (and typically inflated). The Content-Type header is ignored.

Repro

curl -X PUT -H "Content-Type: application/octet-stream" \
  --data-binary @font.otf \
  http://localhost:3002/api/projects/<id>/files/assets/font.otf
# → 200, but the file on disk has a different sha256 and a larger size

Observed with a 20 828-byte OTF font arriving as 37 041 bytes on disk; @font-face then falls back silently, which makes this painful to diagnose.

Fix

Read the raw bytes (Buffer.from(await c.req.arrayBuffer())) and write the buffer without an encoding. This is byte-exact for text bodies too (UTF-8 text round-trips unchanged), so existing text uploads keep working — no content-type sniffing needed.

Testing

  • Added a vitest case in files.test.ts: POST + PUT with bodies containing invalid-UTF-8 bytes round-trip byte-for-byte.
  • Verified on a live studio-server deployment: sha256 of an uploaded OTF/PNG now matches the source exactly; HTML/text uploads (including non-ASCII content) unchanged.

c.req.text() decodes the request body as UTF-8, replacing invalid
bytes with U+FFFD — binary uploads (fonts, images) were silently
corrupted on disk. Read the raw bytes and write the buffer instead;
byte-exact for text bodies too.
.arrayBuffer()
.then((b) => Buffer.from(b))
.catch(() => Buffer.alloc(0));
writeFileSync(res.absPath, body);
@jrusso1020

Copy link
Copy Markdown
Collaborator

Continuing this contribution in #3653, rebuilt on current main with regression coverage and attribution to this PR. Keeping this PR open until the successor lands. Thanks for the original fix.

@jrusso1020

Copy link
Copy Markdown
Collaborator

Closing as implemented in #3653, now merged. The successor preserves this contribution while updating it for current main and adding regression coverage. Thanks for the original fix.

@jrusso1020 jrusso1020 closed this Sep 4, 2026
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.

4 participants