[Security] Unhandled exception in .metadata.json parsing terminates ranger
Summary
| Title | Unhandled exception in .metadata.json parsing terminates ranger |
|---|---|
| CWE | CWE-248 (Uncaught Exception) |
| Affected | ranger 1.9.4 (git 51e19b8) and likely all prior versions |
| Tested | ranger 1.9.4, Python 3.13.12, x86_64 Linux |
When ranger browses a directory containing a malformed .metadata.json file, the JSON parser raises a ValueError that is not caught anywhere in the draw call chain. The exception propagates to the outermost handler in main.py, which prints a traceback and exits. The user loses their ranger session without any warning or recovery prompt.
Description
_get_metafile_content() in ranger/core/metadata.py reads and parses .metadata.json files. When json.load() fails, it re-raises as a ValueError (line 126):
except ValueError:
raise ValueError("Failed decoding JSON file %s" % metafile)The public API get_metadata() only guards against KeyError. ValueError is not caught at any level of the call chain:
def get_metadata(self, filename):
try:
return ostruct(copy.deepcopy(self.metadata_cache[filename]))
except KeyError:
try:
return ostruct(copy.deepcopy(self._get_entry(filename)))
except KeyError: # ValueError not caught here
return ostruct()The exception propagates upward through the draw stack:
_get_metafile_content()
-> _get_entry()
-> get_metadata()
-> browsercolumn._draw_directory()
-> DisplayableContainer.draw()
-> ui.draw()
-> ui.redraw()
-> fm.loop()
-> main.main() [outer except Exception: prints traceback, exits]A malformed .metadata.json can be placed in any shared or world-writable directory (e.g. /tmp, a project repository, a downloaded archive), requiring no elevated privileges. Accidentally malformed files from third-party tools that write a .metadata.json with non-standard content trigger the same crash.
Steps to Reproduce
Create a test directory with a regular file:
mkdir /tmp/testdir echo "example file" > /tmp/testdir/file.txtWrite a malformed
.metadata.jsonin the same directory:echo 'INVALID JSON <<<' > /tmp/testdir/.metadata.jsonOpen ranger and navigate into
/tmp/testdir/.To reproduce without the ranger TUI, run directly via Python:
import sys; sys.path.insert(0, '/path/to/ranger') from ranger.core.metadata import MetadataManager MetadataManager().get_metadata('/tmp/testdir/file.txt')Observe that an unhandled
ValueErroris raised. In a live ranger session this exception terminates the process.
Error Output
Traceback (most recent call last):
File "ranger/core/metadata.py", line 124, in _get_metafile_content
entries = json.load(fobj)
...
File "ranger/core/metadata.py", line 45, in get_metadata
return ostruct(copy.deepcopy(self._get_entry(filename)))
File "ranger/core/metadata.py", line 99, in _get_entry
entries = self._get_metafile_content(metafile)
File "ranger/core/metadata.py", line 126, in _get_metafile_content
raise ValueError("Failed decoding JSON file %s" % metafile)
ValueError: Failed decoding JSON file /tmp/testdir/.metadata.jsonSuggested Fix
Add ValueError to the exception tuple in the inner except clause of get_metadata() so that a JSON parse failure is handled gracefully and returns an empty metadata struct instead of propagating:
--- ranger/core/metadata.py (original)
+++ ranger/core/metadata.py (fixed)
def get_metadata(self, filename):
try:
return ostruct(copy.deepcopy(self.metadata_cache[filename]))
except KeyError:
try:
return ostruct(copy.deepcopy(self._get_entry(filename)))
- except KeyError:
+ except (KeyError, ValueError):
return ostruct()This fix was applied and confirmed: get_metadata() now returns an empty DefaultOpenStruct without raising when .metadata.json contains invalid JSON. ranger continues operating normally and the file's entry is displayed without metadata.
Credits
This issue was identified by Michał Majchrowicz and Marcin Wyczechowski, members of the AFINE Team.
Disclosure Policy
We follow a 90-day coordinated disclosure policy. We are committed to working with the ranger maintainers to resolve this issue before any public disclosure. If we do not receive a response within 90 days of the initial report, we reserve the right to publicly disclose the details of this vulnerability.
Source: ranger/ranger