fix(path): support Bun through Rust canonicalization - #324
Conversation
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: blocked before merge. Reviewed September 13, 2026, 4:43 PM ET / 20:43 UTC (Revision 2). ClawSweeper reviewWhat this changesAdds Bun compatibility through Rust-backed POSIX path resolution, preserves Windows recursive-directory path spelling, and updates runtime qualification, tests, and documentation. Merge readiness⛔ Blocked before merge - 4 items remain This remains useful work absent from current main, but the previously reported workspace-admission defect is still unfixed. The latest buffer-comparison change and reported timings address the earlier timeout concern. Priority: P2 Review scores
Verification
How this fits togetherfs-safe turns caller-supplied paths into guarded filesystem operations. Its canonicalization layer feeds root confinement, identity checks, secret handling, and temporary-workspace admission. flowchart TD
A[Caller paths] --> B[Runtime and native policy]
B --> C[Bun POSIX Rust resolver]
B --> D[Runtime resolver]
C --> E[Confinement and identity checks]
D --> E
E --> F[Guarded filesystem operations]
Before merge
Findings
Agent review detailsSecurityNeeds attention: Required-helper admission remains bypassable for temporary workspaces; no additional supply-chain concern was found. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Reject unavailable required canonicalization before workspace directory creation while preserving Node, native-off, and ordinary missing-directory behavior. Do we have a high-confidence way to reproduce the issue? Yes, from source: on Bun POSIX, require mode with an unavailable addon or canonicalizer reaches catch-all workspace admission and continues to mkdir/mkdtemp. This review did not execute the failing path. Is this the best way to solve the issue? The shared resolver is a reasonable compatibility boundary, but the patch is incomplete until workspace admission propagates its required-helper failure. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against 5bc1f88d6743. LabelsLabel justifications:
EvidenceSecurity concerns:
What I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (1 earlier review cycle)
|
## What Problem This Solves The Bun resolver comments did not explain why its workaround uses the existing Rust addon or why disabling that addon restores Bun's upstream limitations. ## Why This Change Was Made Document the N-API choice beside the resolver: it works with JIT disabled and avoids a separate FFI bridge for native loading and memory handling. Clarify that native mode `off` disables the workaround because it belongs to the same optional addon. ## User Impact Source comments only; runtime behavior and public contracts are unchanged. Follows #324. No changelog entry is needed for this explanatory follow-up. ## Evidence Every non-comment line is identical to the merged implementation. `git diff --check` passes. The implementation's passing Node/Bun checks, platform CI, and stress evidence remain recorded in #324; this comment-only change does not invalidate that proof.
What Problem This Solves
Fixes an issue where fs-safe consumers on Bun reject restrictive files and sockets, confuse literal POSIX backslashes with path separators, or resolve symlink/parent components in the wrong order. On Windows, Bun also rejects existing relative directories during recursive mkdir, breaking atomic file publication and queued JSON writes.
Why This Change Was Made
All default canonicalization now goes through one internal owner. On Bun macOS/Linux it uses the existing Rust N-API addon: the system
realpathfor native resolution and a component walk for ordinary resolution; Node and Windows retain their runtime resolvers. Ordinary resolution normalizes both initial paths and expanded symlink targets, while native resolution preserves their order. A documented 1,024-expansion limit rejects fixed and growing lexical cycles withELOOP. Confinement, pinned descriptors, and post-operation identity checks remain with their existing owners. The Rust resolver never opens the leaf, preserving restrictive permissions, sockets, and POSIX record locks.Windows recursive mkdir receives an absolute path without collapsing raw components or logical junction spelling. Explicit caller-supplied filesystem adapters keep their contracts. Portable test fixtures stop depending on Node-only module synchronization, async-hook internals, enumeration order, and
fs.accessreturn values.User Impact
Bun 1.4.2 works with the matching addon, including
bun --jitless. There is nobun:ffi, dynamic libc discovery, pointer management in TypeScript, new dependency, or public API change.Native mode
offstill prevents addon loading. Bun POSIX with native disabled, or without the addon inauto, retains the runtime's path/permission limitations; Node remains the option for full addon-free compatibility. On Bun POSIX,requirerejects canonicalization when the addon/capability is unavailable. These limits are documented instead of weakening identity checks or silently enabling native code.Related upstream PRs: oven-sh/bun#42374 and oven-sh/bun#42635. Neither upstream fix is assumed released.
Evidence
Final commit
bdd6055ae20da0a0a6f1402ee87c45135a17cad9: CI and coverage passed, including native Bun and JIT-disabled package proof on Windows/macOS/Linux, Rust checks, package smoke, and Node 22/24 checks. Independent review is clean through P2.Full Node
pnpm check: 9,049 passed, 109 platform skips, including build, documentation, and package checks.Bun native qualification: 879 passed, 19 platform skips across 36 test files.
Built-package proof under normal and JIT-disabled Bun:
auto,require,off, and actual external consumers with native packages omitted. Covers Root reads/writes, confinement, hashing, secrets with modes 000/200, search-only directories, sockets, backslash collisions, and raw symlink/parent paths.Refreshed macOS/APFS stress: 57,600 file hash/metadata checks, 540 Root copies, 543 hashes, 96 cancellation operations, no writes after settlement, descriptors 7 → 7.
Linux x64 glibc 2.43 and musl 1.2.5: freshly built final Rust addons each passed 875 Bun tests (23 platform skips), record-lock/cycle regressions, and all normal/JIT-disabled package scenarios.
Fixed Bun CI timeouts in large-buffer deep equality: exact
Buffer.equalscomparisons preserve every byte assertion and reduced the affected local tests from 496/445 ms to 15/6 ms without changing timeouts.Rust workspace tests and Clippy pass. Record-lock regression includes an open/close positive control; nested symlink-target normalization and both fixed/growing lexical cycles have regressions. Review caught the symlink normalization and cycle cases; both were repaired and rechecked.
The exhaustive Bun diagnostic deliberately remains separate: 8,919 passed, 130 failed, 109 skipped before the copy-loader fixture adjustment. Failures expose unsupported native-off cases and missing/capability mock assumptions; they are not marked as expected passes. Node CI retains all fallback assertions.
Tests added or updated when behavior changed
Security and compatibility impact considered
CHANGELOG.mdupdated when release-relevantNo credentials, private paths, private hosts, or sensitive contents included