2929 AIOHTTP_CLIENT_ALLOW_REDIRECTS ,
3030 AIOHTTP_CLIENT_SESSION_SSL ,
3131 AIOHTTP_CLIENT_TIMEOUT ,
32+ BYPASS_RETRIEVAL_ACCESS_CONTROL ,
3233 ENABLE_FORWARD_USER_INFO_HEADERS ,
3334 OFFLINE_MODE ,
3435)
@@ -115,6 +116,7 @@ def build_loader_from_config(request):
115116 MINERU_API_KEY = config .MINERU_API_KEY ,
116117 MINERU_API_TIMEOUT = config .MINERU_API_TIMEOUT ,
117118 MINERU_PARAMS = config .MINERU_PARAMS ,
119+ MINERU_FILE_EXTENSIONS = config .MINERU_FILE_EXTENSIONS ,
118120 )
119121
120122
@@ -1252,11 +1254,27 @@ async def get_sources_from_items(
12521254 ],
12531255 }
12541256 else :
1255- # Fallback to collection names
1256- if item .get ('legacy' ):
1257- collection_names .append (f'{ item ["id" ]} ' )
1258- else :
1259- collection_names .append (f'file-{ item ["id" ]} ' )
1257+ # Chunked-retrieval fallback — verify read access before
1258+ # exposing the file's vector collection (same posture as the
1259+ # full-context branch above).
1260+ file_id = item .get ('id' )
1261+ if file_id :
1262+ if BYPASS_RETRIEVAL_ACCESS_CONTROL :
1263+ if item .get ('legacy' ):
1264+ collection_names .append (f'{ file_id } ' )
1265+ else :
1266+ collection_names .append (f'file-{ file_id } ' )
1267+ else :
1268+ file_object = await Files .get_file_by_id (file_id )
1269+ if file_object and (
1270+ user .role == 'admin'
1271+ or file_object .user_id == user .id
1272+ or await has_access_to_file (file_id , 'read' , user )
1273+ ):
1274+ if item .get ('legacy' ):
1275+ collection_names .append (f'{ file_id } ' )
1276+ else :
1277+ collection_names .append (f'file-{ file_id } ' )
12601278
12611279 elif item .get ('type' ) == 'collection' :
12621280 # Manual Full Mode Toggle for Collection
@@ -1302,9 +1320,21 @@ async def get_sources_from_items(
13021320 'metadatas' : [metadatas ],
13031321 }
13041322 else :
1305- # Fallback to collection names
13061323 if item .get ('legacy' ):
1307- collection_names = item .get ('collection_names' , [])
1324+ if BYPASS_RETRIEVAL_ACCESS_CONTROL :
1325+ collection_names = item .get ('collection_names' , [])
1326+ else :
1327+ # Legacy KB: item.collection_names is client-supplied.
1328+ # Validate against the KB's actual files to prevent
1329+ # cross-tenant collection name substitution.
1330+ files = await Knowledges .get_files_by_id (knowledge_base .id )
1331+ owned_names = {f'file-{ f .id } ' for f in files }
1332+ owned_names .add (knowledge_base .id )
1333+ valid_names = [
1334+ n for n in (item .get ('collection_names' ) or [])
1335+ if n in owned_names
1336+ ]
1337+ collection_names = valid_names if valid_names else [knowledge_base .id ]
13081338 else :
13091339 collection_names .append (item ['id' ])
13101340
@@ -1315,11 +1345,22 @@ async def get_sources_from_items(
13151345 'metadatas' : [[doc .get ('metadata' ) for doc in item .get ('docs' )]],
13161346 }
13171347 elif item .get ('collection_name' ):
1318- # Direct Collection Name
1319- collection_names .append (item ['collection_name' ])
1348+ if BYPASS_RETRIEVAL_ACCESS_CONTROL :
1349+ collection_names .append (item ['collection_name' ])
1350+ else :
1351+ log .debug (
1352+ "get_sources_from_items: ignoring untrusted direct "
1353+ "collection_name '%s' on item without type" ,
1354+ item .get ('collection_name' ),
1355+ )
13201356 elif item .get ('collection_names' ):
1321- # Collection Names List
1322- collection_names .extend (item ['collection_names' ])
1357+ if BYPASS_RETRIEVAL_ACCESS_CONTROL :
1358+ collection_names .extend (item ['collection_names' ])
1359+ else :
1360+ log .debug (
1361+ "get_sources_from_items: ignoring untrusted direct "
1362+ "collection_names on item without type" ,
1363+ )
13231364
13241365 # If query_result is None
13251366 # Fallback to collection names and vector search the collections
0 commit comments