Files
hermes-agent/tests/plugins/test_plugin_loader_unreadable.py
ethernet c1807fc570 test(plugins): fail the pathlib probes the loader guards, not os.stat
Path.exists/is_dir swallow PermissionError into False on 3.12+, so a stat
patch never reached the loader's except branch.
2026-09-19 04:43:13 -04:00

38 lines
1.5 KiB
Python

"""One mode-000 / ACL-denied child under a plugin root must not abort the listing (#111804)."""
from __future__ import annotations
from pathlib import Path
from plugins import plugin_loader
def _deny(monkeypatch, denied: Path) -> None:
"""chmod 000 does not bite as root, so fail the probes of the denied child's ``__init__.py``.
``Path.exists``/``is_dir`` route through ``os.path`` on 3.12+, which swallows
``PermissionError`` into False; the loader's own guard sees the error only when the
pathlib probe raises, so fail both layers the way a real ACL denial can."""
real_exists, real_is_dir = Path.exists, Path.is_dir
def refuse(method):
def probe(self, *args, **kwargs):
if self.parent == denied or self == denied:
raise PermissionError(13, "Permission denied", str(self))
return method(self, *args, **kwargs)
return probe
monkeypatch.setattr(Path, "exists", refuse(real_exists))
monkeypatch.setattr(Path, "is_dir", refuse(real_is_dir))
def test_iter_plugin_dirs_skips_unreadable_child(tmp_path, monkeypatch, caplog):
for name in ("denied", "good"):
(tmp_path / name).mkdir()
(tmp_path / name / "__init__.py").write_text("", encoding="utf-8")
_deny(monkeypatch, tmp_path / "denied")
with caplog.at_level("WARNING", logger="plugins.plugin_loader"):
assert plugin_loader.iter_plugin_dirs(tmp_path) == [tmp_path / "good"]
assert "Skipping unreadable plugin directory" in caplog.text