fix: address PR #813 review round 4 (log leak, cross-user chunk ctx, algo, overlap)

1. Don't log unverified result titles: both search algorithms logged top-5
   titles at DEBUG before verify-on-read; with owner-level share expansion the
   unverified set can contain other users' docs. Algorithms now log a count
   only; the verifying callers (server/semantic, viz_routes, api/visualization)
   log verified titles after verify-on-read.

2. Cross-user FILE chunk context: get_chunk_with_context + the Qdrant chunk
   helpers now take accessible_owners and use build_ownership_filter. For files
   the expanded scope is honoured only after a per-file file_accessible_by_id
   check (accessible_owners is owner-level, so the gate prevents a one-file
   share recipient from reading any of the owner's cached chunks). note/deck/
   news stay self-only (per-user APIs) — a documented gap. Both chunk endpoints
   pass accessible_owners.

3. Algorithm usage: SemanticSearchAlgorithm is not dead (it backs the dense-only
   option on the viz/API surfaces); added a clarifying comment in server/
   semantic.py. Additionally wired accessible_owners + verify-on-read into the
   /api/v1 search routes (unified_search, vector_search) so the astrolabe
   surface is ACL-aware too — degrading gracefully to self-only/unverified for
   non-provisioned callers instead of 401.

4. Overlapping conditions: build_ownership_filter no longer lists self in the
   owner_id MatchAny branch (self is already covered by the user_id branch);
   the owner_id branch carries only the OTHER owners.

Tests: build_ownership_filter dedup + chunk-bbox filter-shape updates; new
ACL-aware get_indexed_doc_types, cached-chunk lookup, and end-to-end cross-user
file chunk-context (recipient gets the chunk, non-recipient denied) tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-05-29 16:55:00 +02:00
co-authored by Claude Opus 4.8
parent ac041d5c97
commit 8deb48e6fa
11 changed files with 393 additions and 87 deletions
+17 -1
View File
@@ -129,7 +129,12 @@ def configure_semantic_tools(mcp: FastMCP):
accessible_owners = await list_accessible_owners(client.sharing, username)
try:
# Create BM25 hybrid search algorithm with specified fusion
# The nc_semantic_search tool deliberately uses BM25-hybrid (dense +
# sparse with RRF/DBSF fusion) as the single tool-layer algorithm.
# SemanticSearchAlgorithm is not dead code — it backs the dense-only
# option that the visualization/API surfaces expose explicitly
# (auth/viz_routes.py and api/visualization.py). Both algorithms take
# accessible_owners, so ACL-aware search works on every surface.
search_algo = BM25HybridSearchAlgorithm(
score_threshold=score_threshold, fusion=fusion
)
@@ -230,6 +235,17 @@ def configure_semantic_tools(mcp: FastMCP):
verified_chunk_count,
dropped_count,
)
# Safe to log titles now: these results passed verify-on-read, so the
# caller is confirmed to have access (unverified titles were never
# logged — see the search algorithms).
if verified_results:
logger.debug(
"Top verified results: %s",
", ".join(
f"{r.doc_type}_{r.id} (score={r.score:.3f}, title='{r.title}')"
for r in verified_results[:5]
),
)
search_results = verified_results[:limit]
# Convert SearchResult objects to SemanticSearchResult for response.