Skip to content

Bound layer decompression during staging and extraction - #117

Draft
Bruno Borges (brunoborges) with Copilot wants to merge 3 commits into
mainfrom
copilot/fix-layer-extraction-decompression-size-cap
Draft

Bruno Borges (brunoborges) with Copilot wants to merge 3 commits into
mainfrom
copilot/fix-layer-extraction-decompression-size-cap

Conversation

Copilot AI commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Three copy loops moved layer bytes with unbounded io.Copy, each suppressed from gosec with a //nolint:gosec // trusted layer content comment. Layer content is publisher-supplied and only digest-verified, so a gzip or sparse-tar bomb inside a legitimate image could decompress without limit into the shared staging root (/tmp/brewlet-runnable/...) or the bundle's classpath dirs.

Changes

  • core/internal/artifact/blobs.go
    • Adds MaxLayerDecompressedBytes (8 GiB ceiling, lowerable by tests) and CopyBounded(dst, src, limit), which fails loudly once the limit is breached instead of expanding a bomb.
    • gunzipToFile copies through the cap and removes the half-written layer on breach, so a failed stage leaves nothing behind.
    • extractGzTar spends one budget across all entries (including the trailing gzip drain at EOF), so many small entries cannot bypass a per-entry cap.
  • core/internal/runtime/launch.go
    • extractTar uses artifact.CopyBounded with the same per-tar budget.
  • All three //nolint:gosec suppressions are removed — the G110 finding they hid is now addressed.
  • Tests cover cap enforcement on all three sites, an accept-within-cap case, and partial-output cleanup.
func CopyBounded(dst io.Writer, src io.Reader, limit int64) (int64, error) {
	n, err := io.Copy(dst, io.LimitReader(src, limit+1))
	if err != nil {
		return n, err
	}
	if n > limit {
		return n, fmt.Errorf("layer exceeds decompression cap of %d bytes", MaxLayerDecompressedBytes)
	}
	return n, nil
}

The cap is an availability bound, not a payload constraint; per-layer limits could later ride the manifest's declared sizes where available.

Copilot AI and others added 2 commits September 21, 2026 20:15
Co-authored-by: brunoborges <129743+brunoborges@users.noreply.github.com>
Co-authored-by: brunoborges <129743+brunoborges@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix decompression size cap in layer extraction Bound layer decompression during staging and extraction Sep 21, 2026

This branch has not been deployed

No deployments
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.

Layer extraction/gunzip has no decompression size cap (io.Copy //nolint:gosec "trusted layer content")

2 participants