#22401: fix: resolve relative media paths against workspace and fix /tmp on macOS
channel: whatsapp-web
size: L
Cluster:
Media Handling Improvements
## Problem
`assertLocalMediaAllowed` rejects valid media paths in two scenarios:
1. **Relative paths** like `projects/file.pdf` are resolved via `path.resolve()` against `process.cwd()`, not the agent workspace. Since the workspace is in the allowed roots but cwd may differ, the check fails.
2. **`/tmp` on macOS** is a symlink to `/private/tmp`. `fs.realpath("/tmp/file.pdf")` returns `/private/tmp/file.pdf`, but the allowed root uses `os.tmpdir()` = `/var/folders/.../T`. Neither matches.
Fixes #22398
## Changes
### `src/media/local-roots.ts`
- Add `safeRealpath()` helper to resolve roots consistently via `fs.realpathSync`
- Resolve `preferredTmpDir` through realpath so symlinked dirs match
- Explicitly add `/tmp`'s realpath as an allowed root (handles macOS `/private/tmp`)
### `src/web/media.ts`
- Add optional `cwd` parameter to `assertLocalMediaAllowed` for resolving relative paths
- Resolve relative paths against `cwd` (agent workspace) instead of `process.cwd()`
- Add `cwd` option to `WebMediaOptions` and pass through from `loadWebMedia`
## Before
```
message send filePath="downloads/file.pdf" → ERROR: not under allowed directory
message send filePath="/tmp/file.pdf" → ERROR: not under allowed directory (macOS)
```
## After
```
message send filePath="downloads/file.pdf" → resolves against workspace, allowed ✅
message send filePath="/tmp/file.pdf" → /private/tmp matched via realpath ✅
```
## Notes
- Callers that pass `localRoots` with agent-scoped roots should also pass `cwd` for relative path resolution
- No behavior change for absolute paths already under allowed roots
- Security: relative paths are still validated against the allowed roots after resolution
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds infrastructure for resolving relative media paths against agent workspace and fixes macOS `/tmp` symlink handling, but the relative path fix is incomplete.
**Changes:**
- Adds `safeRealpath()` helper to consistently resolve directory paths through symlinks
- Resolves `preferredTmpDir` and explicitly adds `/tmp`'s realpath to allowed roots (fixes macOS `/private/tmp` symlink issue)
- Adds optional `cwd` parameter to `assertLocalMediaAllowed()` and `WebMediaOptions` for resolving relative paths
**Issues found:**
- The `cwd` parameter infrastructure is added but no callsites are updated to pass the agent workspace directory. Relative paths will continue to be resolved against `process.cwd()` instead of the agent workspace until callers are updated.
**Impact:**
- macOS `/tmp` fix is complete and working
- Relative path resolution fix is incomplete - requires follow-up to update callsites
<h3>Confidence Score: 3/5</h3>
- This PR is partially complete - the macOS `/tmp` fix is solid, but the relative path resolution requires follow-up changes to callsites
- The macOS symlink handling is correctly implemented and will work immediately. However, the relative path resolution fix only adds infrastructure without updating any callsites to pass the `cwd` parameter. This means the primary issue described in the PR (relative paths resolved against wrong directory) remains unfixed until callers are updated. The changes are safe and don't introduce regressions, but incomplete.
- Check `src/web/outbound.ts`, `src/infra/outbound/deliver.ts`, and other files that call `loadWebMedia()` with agent-scoped local roots - these need to be updated to pass `cwd` parameter
<sub>Last reviewed commit: f1c2f6d</sub>
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#9817: fix(media): resolve relative paths before reading local files (#8759)
by lailoo · 2026-02-05
84.7%
#7400: media: allow temp-dir MEDIA paths for tool outputs
by grammakov · 2026-02-02
79.1%
#22178: test(web): allow fixture roots in media local file tests
by Kansodata · 2026-02-20
79.0%
#22910: fix(browser): resolve symlinks in upload path validation
by erdinccurebal · 2026-02-21
78.9%
#22356: test(web): fix media test fixture local root handling
by AIflow-Labs · 2026-02-21
78.3%
#23308: fix(browser): accept upload paths that traverse symlinked tmp dirs
by SidQin-cyber · 2026-02-22
78.2%
#20294: fix(message): thread mediaLocalRoots through channel plugin dispatch
by odrobnik · 2026-02-18
76.8%
#17456: fix(test): stabilize media root guard test against tmpdir HOME overlap
by widingmarcus-cyber · 2026-02-15
76.7%
#19171: fix(feishu): pass mediaLocalRoots to sendMediaFeishu for agent-scop...
by whiskyboy · 2026-02-17
76.6%
#19325: feat: support messages.mediaLocalRoots for custom media directories
by deggertsen · 2026-02-17
76.3%