This commit is contained in:
Tom Moor
2026-05-10 22:28:18 -04:00
parent 77acc649f8
commit 5e64cabdc4
3 changed files with 3 additions and 20 deletions
@@ -651,7 +651,7 @@ export default class PostgresSearchProvider extends BaseSearchProvider {
const filter = options.filter;
// Visibility predicate for User contexts. A row is visible if any of:
// A document is visible if any of:
//
// - direct membership on the document
// - group membership on the document
@@ -663,12 +663,6 @@ export default class PostgresSearchProvider extends BaseSearchProvider {
// - the user is the creator AND the doc has no collection (unplaced
// drafts that no membership/collection check can reach)
//
// This is load-bearing for authorization. Do not derive the collection
// arm from the filter's collectionId leaves — that's the OR-bypass that
// broke `documents.list`. Authorization for any collectionId referenced
// in the filter has already been enforced by `authorizeFilterFields` in
// the route handler; this clause is the safety net.
//
// For Team contexts (share-based search), the caller is privileged and
// has done its own authorization — narrow to the filter's collectionId
// if specified, otherwise to the team's publicly-permissioned set.
-3
View File
@@ -52,9 +52,6 @@ const COMPARISON_OPERATORS = new Set<ComparisonOperator>([
* relative-past durations (`-P7D` → `now() - 7 days`) and relative-future
* durations (`P7D` → `now() + 7 days`).
*
* The duration is regex-validated to contain only `P`, `T`, digits, and the
* unit letters `YMWDHS`, so the literal interpolation is safe by construction.
*
* @param duration an ISO 8601 duration, optionally signed with a leading `-`.
* @returns a Sequelize literal that resolves to `now() ± interval '<duration>'`.
* @throws if the input is not a valid ISO 8601 duration.
+2 -10
View File
@@ -225,9 +225,9 @@ router.post(
// Exclude archived docs by default. Suppressed when the caller targets a
// specific status, or when their filter already references archivedAt.
const filterMentionsArchivedAt =
const filterIncludesArchivedAt =
filter !== undefined && hasFieldInFilter(filter, "archivedAt");
if (!statusFilter && !filterMentionsArchivedAt) {
if (!statusFilter && !filterIncludesArchivedAt) {
where[Op.and].push({ archivedAt: { [Op.eq]: null } });
}
@@ -259,14 +259,6 @@ router.post(
where[Op.and].push(buildWhere<Document>(mapped));
}
// Visibility predicate: a row is visible to the user if it lives in a
// collection they can access, or if it is one of their own unplaced
// drafts (collectionId is null). This is load-bearing for authorization
// — do not skip it based on what the filter "looks like", because under
// OR semantics any apparent narrowing collapses. The only exceptions are
// the backlink lookup (which scopes by `id` to relationship-derived
// documents) and the parent-doc membership escape (which intentionally
// surfaces children of an accessible parent across collection boundaries).
if (!backlinkDocumentId && !collectionScopeDropped) {
const collectionIds = await user.collectionIds();
where[Op.and].push({