Skip to content

Scope RAG document reads to their repository and owner - #1180

Open
batzela wants to merge 2 commits into
developfrom
fix/rag-document-cross-repository-authorization
Open

Scope RAG document reads to their repository and owner#1180
batzela wants to merge 2 commits into
developfrom
fix/rag-document-cross-repository-authorization

Conversation

@batzela

@batzela batzela commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

get_document and download_document authorized the repository named in the request path, then resolved the document with doc_repo.find_by_id(). That lookup goes through the document_index GSI, which is keyed on document_id alone, so it returns a document regardless of which repository holds it or who owns it. A caller with access to any one repository could name that repository in the path, supply a document id from a repository they cannot access, and receive the document metadata plus a presigned S3 download URL.

Both handlers now resolve documents through _get_authorized_document, which requires that the document belong to the repository named in the path and that the caller own it, or be an admin or RAG admin -- the same ownership rule the delete path already enforced via _ensure_document_ownership. Both paths now share _caller_owns_document so they cannot drift apart.

Issue #, if available:

Description of changes:

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

get_document and download_document authorized the repository named in the
request path, then resolved the document with doc_repo.find_by_id(). That
lookup goes through the document_index GSI, which is keyed on document_id
alone, so it returns a document regardless of which repository holds it or
who owns it. A caller with access to any one repository could name that
repository in the path, supply a document id from a repository they cannot
access, and receive the document metadata plus a presigned S3 download URL.

Both handlers now resolve documents through _get_authorized_document, which
requires that the document belong to the repository named in the path and
that the caller own it, or be an admin or RAG admin -- the same ownership
rule the delete path already enforced via _ensure_document_ownership. Both
paths now share _caller_owns_document so they cannot drift apart.

Every denial raises NotFoundException with an identical message rather than
ForbiddenException, so responses cannot be used to probe which document ids
exist or which repository holds them. download_document also rejects a
missing documentId with a validation error instead of looking up None.

Adds 15 tests covering cross-repository reads, cross-user reads, unknown
ids, the admin and RAG admin exemptions, repository scoping that applies
even to admins and owners, and indistinguishable denial responses. One test
runs the real RagDocumentRepository against the deployed table schema,
including the document_index GSI, to confirm the handler denies a request
the GSI resolves. Three existing happy-path tests needed repository_id and
username on their mock documents to remain authorized requests; no existing
assertion changed.
The codespell hook flags userA as a misspelling of "users", which failed the
pre-commit job. Renamed the two test identities to user-alpha and user-bravo,
matching the repo-alpha and repo-bravo names already used in that test, and
reworded the docstring to describe the scenario directly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant