Repository navigation
fix(security): retrieval no longer consults the scope-blind query cache - #533
Conversation
QueryCache's key is (query, top_k, min_similarity, use_hybrid): no workspace, knowledge tier or document filter. retrieve_context looked it up first and stored into it last, on a path that was never reached only because _get_app_cache() returns None. Re-enabling it would have served one workspace's results to another. The dead read and write are removed, and QueryCache's docstring says it must never hold a scoped retrieval's results. Found by the separate review of #527. The regression test hands retrieval a cache answering with another workspace's chunk and fails on the old code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkWPqAcWbxfqz1aCP6Qsdx
Security review (separate agent session, CONTRIBUTING.md)Reviewed the diff of #533 (head Verdict: the change does what it says. It removes a scope-blind read and write and adds no new access path. I found no Medium or higher finding in the diff. There are two Low findings below: one about the new test, and one pre-existing scope gap in the same function that the diff did not touch. What I verified
Checklist
FindingsLow-1: the test's mock is also the embedding cache, so the search runs with a bogus embedding. Low-2 (pre-existing, outside the diff): GraphRAG query expansion in the same Info (no action needed for this PR):
Both are harmless. The new docstring ( 🤖 Generated with Claude Code |
…ecks the search's scope Review Low-1: the mock answered the embedding lookup too, so the search ran on a string as its embedding. It now answers only the query cache's four-argument lookup, and the test asserts that every search ran in the caller's workspace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkWPqAcWbxfqz1aCP6Qsdx
|
Thanks for the review. On the findings:
🤖 Generated with Claude Code |
|



What
A follow-up from the separate security review of #527 (Info: a dormant hazard, not reachable today).
Retriever.retrieve_contextlooked the query up inQueryCachebefore searching, and stored the result in it afterwards. The cache's key is(query, top_k, min_similarity, use_hybrid). It names no workspace, knowledge tier, document filter or additional workspaces, so a hit would serve one workspace's results to another. The path was dead only because_get_app_cache()always returnsNone.Change
retrieve_contextand_store_query_cacheare removed. The embedding cache stays: an embedding depends on text and model only.QueryCache's docstring says its key carries no scope, so it must never hold a scoped retrieval's results. The class itself, its stats and its initialisation are untouched.Removed rather than given a scoped key: the key would need every input that shapes the result, and nothing uses the cache, so a correct key would be speculative code.
Tests
test_no_cached_result_is_served_in_place_of_a_scoped_searchhands retrieval a cache answering with another workspace's chunk. It fails on the old code and passes now.🤖 Generated with Claude Code
https://claude.ai/code/session_01KkWPqAcWbxfqz1aCP6Qsdx