Skip to content

Fix directory containment bypass via Unicode path collisions on macOS#29442

Closed
tomasilluminati wants to merge 2 commits into
bazelbuild:masterfrom
tomasilluminati:master
Closed

Fix directory containment bypass via Unicode path collisions on macOS#29442
tomasilluminati wants to merge 2 commits into
bazelbuild:masterfrom
tomasilluminati:master

Conversation

@tomasilluminati

Copy link
Copy Markdown

Description

Adds PathContainmentPolicy in lib/vfs: a platform-aware "is path X contained in path Y?" check that closes the semantic gap between Java's byte-level String.startsWith comparisons and APFS's inode resolution on macOS.

  • DEFAULT (Linux, Windows): byte-level PathFragment.startsWith, identical to today.
  • DARWIN (case-insensitive APFS): fast-path byte check, then Normalizer.NFKC + toUpperCase().toLowerCase() so NFC/NFD variants, the seven Latin ligatures (ß, , , , , , ), and case-folded names that resolve to the same inode are recognised as contained, with a segment-boundary guard to prevent /foo falsely containing /foobar.

HOST_POLICY is selected once at init: non-Darwin → DEFAULT; Darwin with -Dbazel.darwin.case_sensitive=true or a runtime java.io.tmpdir probe reporting case-sensitive APFS → DEFAULT; otherwise → DARWIN. This avoids over-folding on case-sensitive APFS, where it would accept symlink targets the kernel would resolve to a different inode.

Wired into every destination-escape check in the repository decompressor package: CompressedTarFunction, ZipDecompressor, SevenZDecompressor, ArFunction, CompressedFunction, PatchUtil. StripPrefixedPath keeps its byte-level strip (because relativeTo is byte-level) but exposes a new foldedPrefixCollision() flag for entries that bypass the byte prefix yet fold to it on the host FS.

Motivation

String.startsWith is byte-correct on Linux but not on APFS, which folds NFC/NFD, ligatures, and (by default) case before resolving names. Distinct Java strings therefore resolve to the same inode, allowing an archive entry like pkg_ß/secret.conf to bypass a pkg_ss/ strip prefix, an /Users/Foo/etc symlink to satisfy a /Users/foo/extract containment check, and SECRET.env / secret.env to silently overwrite each other during extraction. Closes the gap at the VFS layer so Linux and Windows builds remain bit-for-bit unchanged. Mitigations cover CWE-176, CWE-22 and CWE-59 on macOS hosts.

Build API Changes

No

Checklist

  • I have added tests for the new use cases (if any).
  • I have updated the documentation (if applicable).

Release Notes

RELNOTES: Fixed a macOS/APFS semantic gap where Unicode normalization, ligature folding, and case-insensitive name resolution could allow path traversal, sandbox escape, or silent overwrites during archive extraction on case-insensitive APFS volumes.

@google-cla

google-cla Bot commented May 1, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@github-actions github-actions Bot added team-ExternalDeps External dependency handling, remote repositiories, WORKSPACE file. team-Core Skyframe, bazel query, BEP, options parsing, bazelrc awaiting-review PR is awaiting review from an assigned reviewer labels May 1, 2026
@meteorcloudy meteorcloudy removed the team-ExternalDeps External dependency handling, remote repositiories, WORKSPACE file. label May 13, 2026
@tomasilluminati

tomasilluminati commented Jun 29, 2026

Copy link
Copy Markdown
Author

Hi! Just bumping this to see if we can get a reviewer assigned.
For some added context, this same underlying root cause was recently published as CVE-2026-49401 for Deno (rated High at 8.4 by NVD). Given the security implications for macOS path containment, it would be great to get eyes on this when someone has a moment. Thanks!

@tomasilluminati

Copy link
Copy Markdown
Author

Hi @meteorcloudy, saw you touched the labels on this back in May. Any chance you know who could take this for review, or who's the right person to loop in on team-Core?

@github-actions github-actions Bot removed the awaiting-review PR is awaiting review from an assigned reviewer label Jul 14, 2026
@tomasilluminati

Copy link
Copy Markdown
Author

Closing this in favor of #30245.
After more testing I reworked both the framing and the fix. The reproducible impact is a silent intra-directory overwrite from an APFS name collision (an integrity issue), not a directory escape, so instead of touching the containment checks the new PR adds a write-time collision check in the decompressors. Full details and tests are there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

team-Core Skyframe, bazel query, BEP, options parsing, bazelrc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants