refac: owner-bind ephemeral web-search RAG collections (#26706)

The web-search-* namespace was the one collection namespace filter_accessible_collections admitted unconditionally for any non-admin user, on both read and write, unlike file-*, user-memory-* and knowledge bases which are owner-scoped. process_web_search now mints these ephemeral per-query collections as web-search-{user.id}-<hash>, and the access helper only admits web-search-{requester.id}-* names, so a web-search collection is readable and writable only by the user who created it (admins keep their bypass). The collections hold transient public web-search results and their names are non-enumerable query hashes, so there was no demonstrated cross-user access path; this removes the namespace exception so the per-user scoping the other namespaces enforce also covers web-search.

Co-authored-by: rexpository <rexpository@users.noreply.github.com>
This commit is contained in:
Classic298
2026-07-27 08:08:04 +02:00
committed by GitHub
parent 3a9b9a1a74
commit 6d4c02a89e
2 changed files with 7 additions and 6 deletions
+5 -5
View File
@@ -1267,7 +1267,7 @@ async def filter_accessible_collections(
- any name with characters outside [A-Za-z0-9_-] → rejected
- file-* → validated via has_access_to_file
- user-memory-* → must match user's own memory collection
- web-search-* → ephemeral per-query collections, always allowed
- web-search-* → ephemeral per-query collections, owner-bound to web-search-{user.id}-*
- knowledge-bases → always denied (system meta-collection)
- everything else → if the name matches a knowledge base, validated
via Knowledges.check_access_by_user_id; if no
@@ -1302,10 +1302,10 @@ async def filter_accessible_collections(
if name == f'user-memory-{user.id}':
validated.add(name)
elif name.startswith('web-search-'):
# Ephemeral collections created by process_web_search — safe
# to allow because they contain only transient web-search
# results scoped to the requesting user's session.
validated.add(name)
# Ephemeral per-query collections, owner-bound: process_web_search mints
# them as web-search-{user.id}-<hash>, so only the creator may read/write.
if name.startswith(f'web-search-{user.id}-'):
validated.add(name)
else:
# May be a knowledge-base ID or a legacy/ephemeral collection.
# If it IS a KB, enforce access control. If no such KB
+2 -1
View File
@@ -2657,7 +2657,8 @@ async def process_web_search(request: Request, form_data: SearchForm, user=Depen
}
else:
# Create a single collection for all documents
collection_name = f'web-search-{calculate_sha256_string("-".join(form_data.queries))}'[:63]
# Bind the ephemeral collection to its owner so filter_accessible_collections can scope it per-user.
collection_name = f'web-search-{user.id}-{calculate_sha256_string("-".join(form_data.queries))}'[:63]
try:
await run_in_threadpool(