Skip to content

fix: prevent symlink-following arbitrary file overwrite in zipx.Unzip (VU#293714)#163

Open
tomasilluminati wants to merge 2 commits into
develar:masterfrom
tomasilluminati:fix/zipx-symlink-following
Open

fix: prevent symlink-following arbitrary file overwrite in zipx.Unzip (VU#293714)#163
tomasilluminati wants to merge 2 commits into
develar:masterfrom
tomasilluminati:fix/zipx-symlink-following

Conversation

@tomasilluminati

@tomasilluminati tomasilluminati commented Jul 8, 2026

Copy link
Copy Markdown

Problem

zipx.Unzip can overwrite files outside the output directory via symlink following. Two gaps:

  • createSymlink passes the archive's link target straight to os.Symlink, with no check that it stays inside the output dir.
  • Regular-file writes open without O_NOFOLLOW, so a write follows a symlink an earlier entry planted at the same path. On case/Unicode-folding volumes (APFS, HFS+) the colliding names need not be byte-identical (ß folds onto ss).

A crafted archive (a symlink pointing outside, then a colliding regular entry) overwrites an arbitrary file the build user can write.

Fix

  • Reject and log any symlink whose resolved target escapes the output dir. Relative targets resolve against the link's directory; both sides are normalized to absolute so a relative --output still compares correctly.
  • Add O_NOFOLLOW to regular-file writes (syscall.O_NOFOLLOW on unix, 0 on windows via a build tag).

Unsafe entries are skipped, not fatal (same as node-tar and bsdtar).

Tests

Regression tests that fail on current master and pass with this change: symlink-escape refusal, no write-through a planted symlink, the single-archive race (0 overwrites over 100 runs), the APFS ss/ß vector (darwin), containment boundary cases, and a benign-archive guard. go vet clean; cross-compiles on linux/windows/darwin (amd64 + arm64).


Update: added the coordination and CI items

Changeset / CHANGELOG. Added .changeset/zipx-symlink-following-fix.md (app-builder-bin, patch). The repo uses changesets, so this makes the check pass and generates the CHANGELOG entry on the next release.

Summary. zipx.Unzip follows symlinks it materializes from the archive, allowing an arbitrary file overwrite outside the output directory. createSymlink writes the entry's target verbatim with no containment check, and regular-file writes open without O_NOFOLLOW, so a write follows a symlink an earlier entry planted at the same path. On case/Unicode-folding volumes (APFS, HFS+) the colliding names need not be byte-identical (ß folds onto ss).

  • CVE: CVE-2026-13723
  • Also tracked as CERT/CC VU#293714
  • CWE-22 (Path Traversal), CWE-59 (Link Following).

Impact. Attacker-controlled content written to an arbitrary path the build user can write (integrity and availability). Local, requires a build to consume a crafted archive.

Breaking changes. None for well-formed archives. Only unsafe entries (symlinks whose resolved target escapes the output dir) are skipped; this is non-fatal, matching node-tar and bsdtar.

Authorship and license. Authored by Tomas Illuminati (@tomasilluminati), coordinated with Laurie Tyzenhaus ( @laurie-tyz ) and CERT/CC under VU#293714. Contributed under the repository's existing MIT license.

@changeset-bot

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 39853c6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
app-builder-bin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@tomasilluminati

Copy link
Copy Markdown
Author

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.

1 participant