Skip to content

fix(security): prevent symlink chain path traversal in archive extraction - #26955

Closed
robobun wants to merge 1 commit into
mainfrom
claude/fix-symlink-chain-traversal
Closed

robobun wants to merge 1 commit into
mainfrom
claude/fix-symlink-chain-traversal

Conversation

@robobun

@robobun robobun commented Feb 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes a symlink chain path traversal vulnerability in libarchive.zig archive extraction that allows writing arbitrary files outside the extraction directory
  • Adds isResolvedPathSafe() which uses realpath to verify symlinks and file paths resolve within the extraction boundary after considering the actual filesystem state
  • Adds post-creation symlink verification (removing unsafe symlinks) and pre-creation parent directory checks for files and directories

Details

The existing isSymlinkTargetSafe() validates symlink targets against logical archive paths using a fake root, but doesn't account for how previously extracted symlinks change filesystem path resolution. A 3-level chain of individually-safe symlinks can escape the extraction directory:

  1. x/a → . (self-referencing, resolves to x/)
  2. x/a/b → . (through first symlink, creates x/b → .)
  3. x/a/b/c → ../.. (through both symlinks, creates x/c → ../.. which escapes)

Each symlink passes isSymlinkTargetSafe() individually, but on the actual filesystem x/c resolves outside the extraction directory. Subsequent file entries through x/c/ write outside the intended directory.

This affects bun create (via extractToDisk) and cross-compilation (via extractToDir), but NOT npm package extraction which already skips non-file entries.

Test plan

  • Concept validation test confirms the symlink chain escapes on the filesystem
  • End-to-end test via bun create with a crafted tarball confirms the fix prevents file writes outside the extraction directory
  • Verified test fails with system bun (vulnerability confirmed) and passes with the fix
  • Build passes (bun bd)

🤖 Generated with Claude Code

…tion

Chained symlinks in a tarball could escape the extraction directory.
The existing isSymlinkTargetSafe() validates symlink targets against
logical archive paths but doesn't consider the actual filesystem state
created by previously extracted entries. A chain of individually-safe
symlinks (e.g., x/a -> ., x/a/b -> ., x/a/b/c -> ../..) can combine
to resolve outside the extraction boundary.

Add isResolvedPathSafe() which uses realpath to verify the actual
filesystem resolution stays within the extraction directory. Apply this
check after creating symlinks (removing unsafe ones) and before creating
files or directories (skipping entries whose parent escapes).

Co-Authored-By: Claude <noreply@anthropic.com>
@claude

claude Bot commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

🤖 Claude is monitoring this PR

I'll review code, respond to comments, and investigate CI failures.
Start any comment with (skip) to have me ignore it.

  • Push fix commits (uncheck to only post review comments)

@coderabbitai

coderabbitai Bot commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@robobun has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 20 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.


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

@robobun

robobun commented Feb 12, 2026 •

Copy link
Copy Markdown
Collaborator Author

@claude

claude Bot commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

Code Review

Newest first

🟡 40200 — 1 issue found

Issue File
🟡 Test file in regression/issue/ without issue number test/regression/issue/symlink-chain-traversal.test.ts

@@ -0,0 +1,130 @@
import { describe, expect, test } from "bun:test";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Minor: This test file is in test/regression/issue/ but doesn't have a GitHub issue number in its filename. Per CLAUDE.md:

If a test is for a specific numbered GitHub Issue, it should be placed in test/regression/issue/${issueNumber}.test.ts. Ensure the issue number is REAL and not a placeholder!
If no valid issue number is provided, find the best existing file to modify instead.

And test/CLAUDE.md:

Regression tests for specific issues go in /test/regression/issue/${issueNumber}.test.ts. If there's no issue number, do not put them in the regression directory.

This should either be renamed to include a real issue number (e.g., test/regression/issue/12345.test.ts) or moved to a more appropriate location like test/js/bun/.

@claude

claude Bot commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@github-actions

Copy link
Copy Markdown
Contributor

Closing this PR because it has been inactive for more than 90 days.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant