Skip to content

test: guard function ZIP extraction contracts - #8543

Open
netlify-coding[bot] wants to merge 2 commits into
mainfrom
netliloop/306/zip-extraction-guards
Open

netlify-coding[bot] wants to merge 2 commits into
mainfrom
netliloop/306/zip-extraction-guards

Conversation

@netlify-coding

@netlify-coding netlify-coding Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Opened by Netliloop run #306 (reduce-change-failure-risk)

Why

Replacing extract-zip moved archive handling into the CLI without direct regression tests. Thirteen deliberate regressions each pass the original 647 unit tests and fail the new suite, exposing gaps in file-write protection, failure cleanup, and extraction compatibility.

What changed

  • Guard file contents against path traversal and existing or archive-created symlinks.
  • Require stream/write failures to reject, close the archive, and stop before later entries.
  • Cover stored/deflated binary data, empty archives, repeated extraction, executable permissions, internal symlinks, and macOS metadata. Only two files under tests/ change; production code is unchanged.

How we verified

  • CI green on 202b06f: unit tests pass on Linux (Node 22/24), macOS and Windows (Node 24); integration and end-to-end pass on Node 22/24. All 38 checks completed: 35 successful, 3 informational neutral. Seven Unix cases skip on Windows.
  • CI=true npm run test:unit: 665 passed on Node 24.15.0. The 18 new tests also pass on Node 22.13.0; every new test has a demonstrated failing mutation control.
  • npm run typecheck, npm run lint, npm run format:check, and git diff --check: passed.
  • Independent Python ZIPs → real ZISI discovery → function registry/extraction → loopback HTTP → V1 function invocation: passed on Node 22 and 24. Exact binary digests, Unicode, symlinks, request/response data, and entrypoint selection were checked. No module mocks; a fixture manifest, offline geolocation, and an Express shell surround the real exported handler.
  • Python's ZIP reader independently validates the new fixture helper's CRCs and metadata. Commands, output, mutation results, and verification limits.
Reproduce the real archive-to-HTTP proof: PASS, with temporary fixtures removed

On Linux/macOS, from this branch with Node ≥22.13.0, npm 11.19.1, and Python 3:

npm ci --no-audit
proof_dir=$(mktemp -d)
curl -fsSL 'https://netliloop.netlify.app/api/files/eyJrZXkiOiJldmlkZW5jZS8zMDYvMWFmMTQ2MzItYzEwYi00NDY3LWFiNjctNmJlYzYzYjQwYzAwIiwibmFtZSI6InZlcmlmeS1pbmRlcGVuZGVudC16aXBzLm10cy50eHQiLCJ0eXBlIjoidGV4dC9wbGFpbiIsImV4cCI6MTgyMjM2NDE2MzkxNX0.QlPalCwgT8xVmIF7PIoPi7fy-wTTBAY-28AVj73NM_o' -o "$proof_dir/verify-independent-zips.mts"
curl -fsSL 'https://netliloop.netlify.app/api/files/eyJrZXkiOiJldmlkZW5jZS8zMDYvY2MwMzc0ZjQtMzdlYi00NzljLWFjZjAtYjVkOTU2MzZmNTQ4IiwibmFtZSI6ImNyZWF0ZS1pbmRlcGVuZGVudC16aXAtZml4dHVyZXMucHkudHh0IiwidHlwZSI6InRleHQvcGxhaW4iLCJleHAiOjE4MjIzNjQxNjQxNzl9.QaZFUgqYBQjvGwaYGpXHCw3QTkKyV8huvpJgWjBUvDY' -o "$proof_dir/create-independent-zip-fixtures.py"
CI=true NETLIFY_TELEMETRY_DISABLED=1 node --import tsx "$proof_dir/verify-independent-zips.mts" "$PWD"

Passing output includes "outcome": "PASS" and "temporaryFixturesRemoved": true. The archive producer intentionally emits one duplicate-name warning.

To reproduce the independent fixture-helper validation, download the Node generator and Python reader under their displayed names into the same proof directory, then run:

node --import tsx "$proof_dir/verify-zip-fixture-helper.mts" "$PWD" "$proof_dir/helper.zip"
python3 "$proof_dir/validate-zip-fixture-helper.py" "$proof_dir/helper.zip"

Passing output starts with PASS: Python zipfile independently validated helper archive.

The mutation harness runs with python3 verify-zip-mutations.py "$PWD"; it temporarily changes only src/utils/zip.ts, refuses pre-existing edits, and restores it in finally. All thirteen original-suite results must exit 0; all thirteen new-suite results must exit 1.

What is left to test

  • Complete CLI startup with these ZIP fixtures and V2 worker execution remain unverified by the new checks; the local integration proof exercises the exported HTTP handler with V1 invocation.
  • A reproduced pre-existing limitation remains: recursive directory creation can create an empty outside directory before canonical-path rejection. These tests guard file writes and do not fix that separate behavior.

Risk

Low — only tests change; incorrect assertions can block CI but cannot alter production behavior.

No Linear issue was created: application authorization returned HTTP 401 on both attempts. Scheduled tracking belongs in FRB, which owns the extraction replacement (FRB-2334). Restore application authorization and create “Guard CLI function ZIP extraction against regressions”, labeled netliloop-pr, with this PR and the low-risk assessment. The separate directory-creation limitation needs follow-up; the primitives issue FRB-2340 is not claimed as fixed.

@netlify-coding
netlify-coding Bot requested a review from a team as a code owner October 1, 2026 04:22
@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Team
  • Run ID: 96d7f626-43a3-4a8d-bd6a-c3766ecc70cf
📥 Commits

Reviewing files that changed from the base of the PR and between 202b06f and fbf278d.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: ccde4fe8-fe3b-4fbe-91b3-c768987ec7f2

📥 Commits

Reviewing files that changed from the base of the PR and between f845e14 and 202b06f.

📒 Files selected for processing (2)
  • tests/helpers/zip.ts
  • tests/unit/utils/zip.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

Included review availability: This review used your included allowance. 5 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 6 reviews per hour. Your free on-demand review promotion remains active until October 9, 2026 at 6:00 PM UTC.

📝 Summary

Summary by CodeRabbit

  • Tests
    • Expanded ZIP extraction test coverage for compressed and uncompressed files, empty archives, metadata handling, overwrites, invalid archives, and path traversal.
    • Added checks for file permissions, symlinks, and cleanup after stream or write failures. Symlink-specific checks are skipped on Windows.
    • Added a test helper for creating ZIP archives with varied entry contents and attributes.

Adds a ZIP buffer helper for test archives. Adds extractZip unit tests for file contents, compression, overwrite behavior, invalid paths and archives, extraction failures, permissions, and symlinks.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Other

Suggested reviewers: amun-sihra

Merge Risk: ⚪ Minimal · up to 202b0

The added ZIP regression tests introduce no established merge-blocking risk. Merge after normal checks; platform-specific execution remains unverified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding tests that guard ZIP extraction behavior and contracts.
Description check ✅ Passed The description directly explains the ZIP extraction regression tests, covered behaviors, verification results, and known limitations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

📊 Benchmark results

Comparing with e8953ec

  • Dependency count: 1,019 (no change)
  • Package size: 380 MB ⬆️ 0.00% increase vs. e8953ec
  • Number of ts-expect-error directives: 280 (no change)

@pkg-pr-new

pkg-pr-new Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/netlify-cli@8543

commit: fbf278d

Comment thread tests/helpers/zip.ts
}

// Build the headers directly so malformed entries and duplicate names survive fixture creation.
export const createZip = (entries: ZipEntry[]): Buffer => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This code is pretty wild. Could we use yazl for this instead? iirc we have a pattern for this in netlify/build.

@serhalp serhalp self-assigned this Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant