Skip to content

Add WithMaxBytes limit to ExtractAll - #35

Merged
andrew merged 3 commits into
mainfrom
extract-byte-limit
Sep 4, 2026
Merged

Add WithMaxBytes limit to ExtractAll#35
andrew merged 3 commits into
mainfrom
extract-byte-limit

Conversation

@andrew

@andrew andrew commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Adds a WithMaxBytes option to ExtractAll that caps the total decompressed bytes written across all entries. The limit is enforced by wrapping each entry copy in io.LimitReader against a running budget, so an archive whose headers under-report uncompressed sizes still cannot write past it. Exceeding the limit returns ErrExtractLimit naming the entry that tripped it.

Needed by git-pkgs/git-pkgs#328, which extracts untrusted registry archives to a temp directory and currently only checks header-declared sizes.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The current limit enforcement can write past the configured cap and the option implementation is stateful across calls when an ExtractOption is reused.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR adds an optional extraction budget to ExtractAll to cap the total decompressed bytes written across all entries, intended to protect callers extracting untrusted archives.

Changes:

  • Add ExtractOption/WithMaxBytes and plumb a shared “remaining bytes” budget through extraction.
  • Introduce ErrExtractLimit and return it (wrapped) when the extraction would exceed the configured budget.
  • Add tests covering the max-bytes behavior for tar and zip inputs.
File summaries
File Description
extract.go Adds the options API and enforces a running decompressed-bytes budget during entry extraction.
extract_test.go Adds tests for budget enforcement and for ignoring header-declared/compressed sizes.
Review details

Suppressed comments (1)

extract.go:211

  • copyWithLimit currently uses io.LimitReader(src, *remaining+1) and copies that extra byte into dst, so extraction can write past the configured cap (and the file on disk can exceed the limit). Also, *remaining+1 can overflow when remaining is math.MaxInt64, causing the limit reader to behave incorrectly.
	// Read one byte past the budget so an entry that would exceed it is
	// detected without draining the whole stream.
	n, err := io.Copy(dst, io.LimitReader(src, *remaining+1))
	if err != nil {
		return n, err
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread extract.go
Comment on lines +39 to +45
func WithMaxBytes(n int64) ExtractOption {
return func(c *extractConfig) {
if n > 0 {
c.remaining = &n
}
}
}
Comment thread extract_test.go Outdated
Comment on lines +453 to +455
if info.Size() > 41 {
t.Fatalf("wrote %d bytes past the limit", info.Size())
}
Caps total decompressed bytes written, enforced against actual bytes
read rather than header-declared sizes.
Store the limit as a value on extractConfig and derive the mutable
budget inside ExtractAll, so reusing one ExtractOption across calls
starts each with a fresh counter. Saturate the +1 sentinel read at
MaxInt64 so the limit reader cannot wrap negative.
Copy exactly the remaining budget then read one byte from src to
detect overflow, so nothing past the cap reaches dst. Drops the +1
and with it the MaxInt64 saturation guard.
@andrew
andrew force-pushed the extract-byte-limit branch from 1716a51 to b6c9b38 Compare September 4, 2026 10:22
@andrew
andrew merged commit b954c3b into main Sep 4, 2026
5 checks passed
@andrew
andrew deleted the extract-byte-limit branch September 4, 2026 10:24
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.

2 participants