Describe the bug
xrspatial/geotiff/_vrt.py:184-187:
```python
if is_relative and not os.path.isabs(filename):
filename = os.path.join(vrt_dir, filename)
Canonicalize to prevent path traversal (e.g. ../)
filename = os.path.realpath(filename)
```
The comment promises path-traversal prevention. realpath only resolves .. and symlinks. The resolved result can still point anywhere on disk: <vrt_dir>/../../etc/passwd resolves to /etc/passwd and the source is then opened by read_to_array. A symlink under vrt_dir to a sensitive file behaves the same way.
For trusted VRTs this is fine. For VRTs accepted from user input (uploads, untrusted mounts) it is a path-traversal primitive.
Expected behavior
When vrt_dir is the trusted root for relative sources, the resolved path must live under realpath(vrt_dir). Absolute paths declared inside the VRT either error out or pass through an explicit opt-in allowlist.
Suggested fix
After realpath, assert the result is under realpath(vrt_dir) using os.path.commonpath or a startswith(root + os.sep) check. Add an environment variable or kwarg for users who legitimately point at sibling directories. Document the new behavior in the read_vrt docstring.
Tests:
- A VRT in
/tmp/vrt_a/foo.vrt referencing ../vrt_b/data.tif raises ValueError.
- The same VRT with an explicit allowlist that names
/tmp/vrt_b succeeds.
- A VRT pointing at a symlink that resolves outside
vrt_dir raises ValueError.
Additional context
Reported during a code review of the geotiff module. The comment on line 186 reads as a security claim that the code does not back up.
Describe the bug
xrspatial/geotiff/_vrt.py:184-187:```python
if is_relative and not os.path.isabs(filename):
filename = os.path.join(vrt_dir, filename)
Canonicalize to prevent path traversal (e.g. ../)
filename = os.path.realpath(filename)
```
The comment promises path-traversal prevention.
realpathonly resolves..and symlinks. The resolved result can still point anywhere on disk:<vrt_dir>/../../etc/passwdresolves to/etc/passwdand the source is then opened byread_to_array. A symlink undervrt_dirto a sensitive file behaves the same way.For trusted VRTs this is fine. For VRTs accepted from user input (uploads, untrusted mounts) it is a path-traversal primitive.
Expected behavior
When
vrt_diris the trusted root for relative sources, the resolved path must live underrealpath(vrt_dir). Absolute paths declared inside the VRT either error out or pass through an explicit opt-in allowlist.Suggested fix
After
realpath, assert the result is underrealpath(vrt_dir)usingos.path.commonpathor astartswith(root + os.sep)check. Add an environment variable or kwarg for users who legitimately point at sibling directories. Document the new behavior in theread_vrtdocstring.Tests:
/tmp/vrt_a/foo.vrtreferencing../vrt_b/data.tifraisesValueError./tmp/vrt_bsucceeds.vrt_dirraisesValueError.Additional context
Reported during a code review of the geotiff module. The comment on line 186 reads as a security claim that the code does not back up.