fix(wiki): reassert cached read-path containment (#4375)

This commit is contained in:
Rod Boev authored and nesquena-hermes committed 2026-06-19 21:11:10 +00:00
1 parent 3f6369ae5c
commit e44fd2a6ac
2 files changed
+51 -1

No files matched your search

+43
View File
@@ -233,6 +233,49 @@ def test_wiki_symlink_to_hidden_same_section_target_blocked(monkeypatch, tmp_pat
assert b"hidden_marker_q" not in h_page.body, "hidden secret leaked via symlink"
def test_wiki_page_cached_nested_entry_rechecks_resolved_containment(monkeypatch, tmp_path):
import os as _os
from api import routes
wiki_root = tmp_path / "wiki"
section = wiki_root / "concepts"
nested = section / "sub"
nested.mkdir(parents=True)
page = nested / "real.md"
page.write_text("# real\n", encoding="utf-8")
secret = wiki_root / ".env"
secret.write_text("DONOTLEAK=stale_cache_marker\n", encoding="utf-8")
try:
page.unlink()
page.symlink_to(_os.path.join("..", "..", ".env"))
except (OSError, NotImplementedError):
import pytest
pytest.skip("symlinks not supported on this platform")
routes._llm_wiki_clear_page_files_cache()
monkeypatch.setattr(routes, "_WIKI_ALLOWLIST_TTL", 60.0)
monkeypatch.setattr(routes, "_llm_wiki_resolve_path", lambda: (wiki_root, None, None))
# Prime the cached name list while the nested page is still valid, then
# swap only the nested entry so the top-level section signature stays stale.
page.unlink()
page.write_text("# real\n", encoding="utf-8")
assert routes._llm_wiki_page_files(wiki_root) == [page]
sig_before = routes._llm_wiki_page_files_cache_signature(wiki_root.resolve())
page.unlink()
page.symlink_to(_os.path.join("..", "..", ".env"))
sig_after = routes._llm_wiki_page_files_cache_signature(wiki_root.resolve())
assert sig_after == sig_before
handler = _FakeHandler()
routes.handle_get(handler, urlparse("http://example.com/api/wiki/page?path=concepts/sub/real.md"))
assert handler.status == 404, f"stale cached nested symlink swap must 404, got {handler.status}"
assert b"stale_cache_marker" not in handler.body, "stale cached nested symlink leaked secret content"
def test_wiki_symlinked_section_cannot_expose_outside_tree(monkeypatch, tmp_path):
"""A symlinked SECTION dir (concepts -> /tmp/outside) must not expose files
outside the real wiki root."""