#3232·ranger

[Security] Unhandled exception in .metadata.json parsing terminates ranger

Author: sectroyerCreated May 20, 2026Updated Aug 24, 2026
Labelsenhancement

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):

python
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:

python
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

  1. Create a test directory with a regular file:

    bash
    mkdir /tmp/testdir
    echo "example file" > /tmp/testdir/file.txt
  2. Write a malformed .metadata.json in the same directory:

    bash
    echo 'INVALID JSON <<<' > /tmp/testdir/.metadata.json
  3. Open ranger and navigate into /tmp/testdir/.

    To reproduce without the ranger TUI, run directly via Python:

    python
    import sys; sys.path.insert(0, '/path/to/ranger')
    from ranger.core.metadata import MetadataManager
    MetadataManager().get_metadata('/tmp/testdir/file.txt')
  4. Observe that an unhandled ValueError is 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.json

Suggested 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:

diff
--- 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.