security(search): ensure that search suggenstion returns answer the user has access to

This commit is contained in:
Edouard Vanbelle
2026-07-16 21:07:18 +02:00
parent 7d95a19907
commit c1924c825b
9 changed files with 171 additions and 83 deletions
+4
View File
@@ -27,11 +27,15 @@ pub trait SearchUseCase: Send + Sync + 'static {
) -> Result<Arc<SearchResultsDto>, DomainError>;
/// Returns quick suggestions for autocomplete (lightweight, fast).
/// `caller_id` scopes results to drives the caller can Read — without
/// it the endpoint leaks names + paths across every tenant on the
/// instance (AuthZ audit finding #1, 2026-07-12).
async fn suggest(
&self,
query: &str,
folder_id: Option<&str>,
limit: usize,
caller_id: Uuid,
) -> Result<SearchSuggestionsDto, DomainError>;
/// Clears the search results cache.
+10 -2
View File
@@ -205,13 +205,21 @@ pub trait FileReadPort: Send + Sync + 'static {
/// Results are ordered by relevance (exact > starts-with > contains) so the
/// caller can use them directly for autocomplete suggestions.
///
/// The default implementation falls back to `list_files` + in-memory filter
/// so that stubs and mocks compile without changes.
/// `caller_id` scopes results to files whose owning drive the caller can
/// Read (direct or group-mediated `role_grants`). Without it the endpoint
/// leaks names + paths across every tenant on the instance — closed as
/// AuthZ audit finding #1 (2026-07-12).
///
/// The default implementation falls back to `list_files` + in-memory
/// filter so that stubs and mocks compile without changes. Stub-mode
/// callers already operate against a single tenant's data, so ignoring
/// `caller_id` here is safe; the PG impl enforces the real scope.
async fn suggest_files_by_name(
&self,
folder_id: Option<&str>,
query: &str,
limit: usize,
_caller_id: Uuid,
) -> Result<Vec<File>, DomainError> {
let all = self.list_files(folder_id).await?;
let q = query.to_lowercase();