mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 10:15:27 -04:00
fix(settings): degrade load_features to defaults on PermissionError
load_settings() already catches PermissionError, but load_features() caught only FileNotFoundError/JSONDecodeError/ValueError. An existing-but-unreadable data/features.json (e.g. root-owned after a deploy) therefore raised instead of falling back to DEFAULT_FEATURES, taking down GET /api/auth/features and anything that reads feature flags. Add PermissionError to the except tuple to match load_settings(). Adds tests/test_load_features_permission_error.py. Co-authored-by: Alexandre Teixeira <111787685+alteixeira20@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
93825a505c
commit
f5c1eb4b9d
+1
-1
@@ -283,7 +283,7 @@ def load_features() -> dict:
|
|||||||
if not isinstance(saved, dict):
|
if not isinstance(saved, dict):
|
||||||
raise ValueError("features must be an object")
|
raise ValueError("features must be an object")
|
||||||
merged = {**DEFAULT_FEATURES, **saved}
|
merged = {**DEFAULT_FEATURES, **saved}
|
||||||
except (FileNotFoundError, json.JSONDecodeError, ValueError):
|
except (FileNotFoundError, PermissionError, json.JSONDecodeError, ValueError):
|
||||||
merged = dict(DEFAULT_FEATURES)
|
merged = dict(DEFAULT_FEATURES)
|
||||||
_features_cache = (now, merged)
|
_features_cache = (now, merged)
|
||||||
return merged
|
return merged
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
"""load_features() must degrade to defaults if features.json is unreadable.
|
||||||
|
|
||||||
|
load_settings() already catches PermissionError, but load_features() did not, so
|
||||||
|
an unreadable data/features.json (e.g. root-owned after a deploy) raised instead
|
||||||
|
of falling back to DEFAULT_FEATURES, taking down GET /api/auth/features.
|
||||||
|
"""
|
||||||
|
import builtins
|
||||||
|
|
||||||
|
import src.settings as settings
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_features_degrades_on_permission_error(monkeypatch):
|
||||||
|
# Ensure the cache does not short-circuit the read.
|
||||||
|
monkeypatch.setattr(settings, "_features_cache", None, raising=False)
|
||||||
|
|
||||||
|
real_open = builtins.open
|
||||||
|
|
||||||
|
def deny(path, *args, **kwargs):
|
||||||
|
if str(path) == str(settings.FEATURES_FILE):
|
||||||
|
raise PermissionError("denied")
|
||||||
|
return real_open(path, *args, **kwargs)
|
||||||
|
|
||||||
|
monkeypatch.setattr(builtins, "open", deny)
|
||||||
|
|
||||||
|
result = settings.load_features()
|
||||||
|
assert result == dict(settings.DEFAULT_FEATURES)
|
||||||
Reference in New Issue
Block a user