#22910: fix(browser): resolve symlinks in upload path validation
size: S
Cluster:
Browser Security Enhancements
## Summary
- On macOS, `/tmp` is a symlink to `/private/tmp` (and `/var` to `/private/var`). The upload path validation in `resolvePathWithinRoot` compared paths using `path.resolve` (which doesn't resolve symlinks), causing legitimate uploads to be rejected when the resolved real path differed from the provided path
- The CLI's `normalizeUploadPaths` calls `resolveExistingPathsWithinRoot` which returns real (symlink-resolved) paths via `openFileWithinRoot`. These real paths were then sent to the server, which re-validated them against the unresolved root directory and rejected them
- Fixed `resolvePathWithinRoot` to resolve both root and target paths to their real form using `fs.realpathSync` before comparison
- Fixed `resolveExistingPathsWithinRoot` to use the real root path when computing relative paths for `openFileWithinRoot`
- Symlink escapes (files that symlink outside the upload root) are still correctly rejected — now caught earlier at the path validation level
Fixes #22848
## Test plan
- [x] All 6 tests in `src/browser/paths.test.ts` pass (including 2 new tests)
- [x] New test: "accepts paths through parent directory symlinks" — verifies uploads work when root dir involves a symlink
- [x] New test: "accepts real paths when root uses a symlinked directory" — verifies the CLI→server flow where real paths are sent
- [x] `src/browser/server.agent-contract-form-layout-act-commands.test.ts` — 11 tests pass (no regression)
- [x] `src/infra/tmp-openclaw-dir.test.ts` — 7 tests pass (no regression)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Resolves symlink handling in upload path validation to fix macOS `/tmp` → `/private/tmp` rejection issue. The fix uses `fs.realpathSync()` to resolve both root and target paths before comparison, ensuring legitimate uploads through symlinked directories work correctly while maintaining security by rejecting symlink escapes.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge - it fixes a legitimate path validation bug while maintaining security guarantees
- The implementation correctly handles symlink resolution edge cases, includes comprehensive test coverage (2 new tests for symlink scenarios + existing tests still pass), and maintains security by rejecting symlink escapes. The fix is well-scoped to the specific issue without introducing unnecessary changes.
- No files require special attention
<sub>Last reviewed commit: ba7ada0</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#23308: fix(browser): accept upload paths that traverse symlinked tmp dirs
by SidQin-cyber · 2026-02-22
90.0%
#18593: fix: resolve symlinks in session path validation (#18553)
by EpaL · 2026-02-16
80.8%
#22401: fix: resolve relative media paths against workspace and fix /tmp on...
by derrickburns · 2026-02-21
78.9%
#8124: fix(browser): add path validation for file upload and download
by yubrew · 2026-02-03
78.8%
#21733: security(exec): platform-aware allowlist matching and restricted sa...
by Esubaalew · 2026-02-20
78.4%
#20390: fix(daemon): fall back to /tmp for launchd logs on removable volumes
by lemoz · 2026-02-18
77.8%
#11529: fix(wizard): strip shell-style backslash escapes from workspace paths
by mcaxtr · 2026-02-07
77.5%
#8718: fix: sanitize download filenames to prevent path traversal (CWE-22)
by DevZenPro · 2026-02-04
77.4%
#8517: Browser: sandbox download/trace paths
by coygeek · 2026-02-04
77.3%
#9154: fix(doctor): resolve symlinks before comparing state directories
by gavinbmoore · 2026-02-04
77.1%