Skip to content

fix: cap total zip expansion during tar conversion#25877

Merged
geokat merged 3 commits into
mainfrom
george/plat-274-zip-upload-decompressed-without-aggregate-size-limit
Jun 2, 2026
Merged

fix: cap total zip expansion during tar conversion#25877
geokat merged 3 commits into
mainfrom
george/plat-274-zip-upload-decompressed-without-aggregate-size-limit

Conversation

@geokat

@geokat geokat commented May 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Reject ZIP uploads whose expanded tar output exceeds the file upload limit.

This change adds aggregate size enforcement when converting ZIP uploads to tar,
so small compressed archives can no longer expand without bound in memory. ZIP
uploads that exceed the configured expansion limit now return
413 Request Entity Too Large, and malformed ZIP content now returns
400 Bad Request instead of surfacing as an internal error.

Changes

  • add archive-level preflight checks for projected tar size
  • add writer-side aggregate limits while streaming tar output
  • propagate tar.Writer.Close() errors instead of dropping them
  • classify malformed ZIP entry metadata and content mismatches as invalid input
  • return 413 from POST /api/v2/files when expanded ZIP content is too large
  • return 400 from POST /api/v2/files for invalid ZIP archive contents
  • add regression coverage for oversized ZIP expansion and invalid ZIP metadata

Ref: https://linear.app/codercom/issue/PLAT-274

@geokat geokat force-pushed the george/plat-274-zip-upload-decompressed-without-aggregate-size-limit branch 7 times, most recently from 1ffbb54 to 8f18ca0 Compare June 1, 2026 21:04
@geokat geokat marked this pull request as ready for review June 1, 2026 21:18
Comment thread archive/archive.go
}

n, err := w.w.Write(p)
w.remaining -= int64(n)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

super micro nit: move this after the if err != nil check that returns the error below.

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.

Thanks! This is intentional: the contract of io.Writer is that Write can return both an error and a valid n, so the wrapper mirrors that behavior by reflecting the best-effort write and then returning the error.

Comment thread archive/archive.go Outdated
return ErrInvalidZipContent
case err != nil:
return err
case written != size:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this case is redundant because io.CopyN docs say

written == n if and only if err == nil

so this is unreachable if err == nil

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.

Good catch; I was too paranoid here :)

Comment thread coderd/files_test.go
// entry header then pushes the converted tar output over the
// file size limit.
size := int64(coderd.HTTPFileMaxBytes - 1024)
zipBytes := buildZipWithSizedFile(t, "oversized.txt", size)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like we could just generate an archive where the metadata claims the expansion would be huge.

Might be splitting hairs here if the memory usage is not a concern.

@geokat geokat Jun 1, 2026

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.

Thanks! I hope it's not a huge concern, because we do want to test the actual streaming enforcement path here (as opposed to the preflight check we'd be testing with a metadata-only test).

@jdomeracki-coder jdomeracki-coder self-requested a review June 2, 2026 09:11

@jdomeracki-coder jdomeracki-coder left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does the job from security perspective

geokat added 3 commits June 2, 2026 09:14
Enforce aggregate limits when converting uploaded ZIP archives to tar
so compressed inputs cannot expand without bound in memory.

Also treat malformed ZIP entry metadata and content mismatches as
client errors during conversion, returning 400 for invalid archives and
413 when expanded tar output exceeds the upload limit.

Ref: https://linear.app/codercom/issue/PLAT-274/zip-upload-decompressed-without-aggregate-size-limit-sec-103
@geokat geokat force-pushed the george/plat-274-zip-upload-decompressed-without-aggregate-size-limit branch from 2db3fa1 to b039620 Compare June 2, 2026 16:15
@geokat geokat merged commit 2f011fd into main Jun 2, 2026
48 of 50 checks passed
@geokat geokat deleted the george/plat-274-zip-upload-decompressed-without-aggregate-size-limit branch June 2, 2026 17:11
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants