Repository navigation
Conversation
added 2 commits
October 27, 2026 13:32
The extracted text of every bitstream in the TEXT bundle was indexed into the item-level Solr fulltext field with no access-control check, while query-time filtering is only item-level. For a public item containing a restricted file, an anonymous keyword search could match words from the restricted file, turning search into a content oracle (and the highlight could return the snippet). FullTextContentStreams now skips any TEXT bitstream unless the Anonymous group has a date-valid READ policy on it. Because the extracted-text derivative receives a one-time copy of its source file's policies (which can later drift), the matching source file in the ORIGINAL bundle is also required to be anonymously readable. The check fails closed on error. Existing Solr documents keep previously leaked text until a full reindex (dspace index-discovery -b) is run.
Resolve the Anonymous group once per FullTextContentStreams instance instead of per bitstream, to avoid repeated lookups during a full reindex. Lookup failures still propagate and cause the text to be skipped (fail closed). Add a unit test for the policy-drift case: an anonymously readable TEXT derivative whose matching source file in the ORIGINAL bundle is restricted must still be excluded from the index.
There was a problem hiding this comment.
🟡 Changes recommended
A critical fail-open path can still index restricted content when the matching source bitstream cannot be resolved.
1 open issue
What changed in this PR
This pull request prevents restricted bitstream text from being indexed in DSpace Discovery’s shared Solr fulltext field by checking Anonymous access to text derivatives and their matching source files.
Changes:
- Adds Anonymous, date-valid read-policy checks for
TEXTderivatives and correspondingORIGINALbitstreams. - Adds unit and integration coverage for restricted derivatives, restricted sources, and publicly readable content.
- Leaves an unresolved access-control gap when a matching source bitstream cannot be resolved.
| File | Description |
|---|---|
dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java |
Verifies Discovery search behavior for restricted and publicly readable file content. |
dspace-api/src/test/java/org/dspace/discovery/FullTextContentStreamsTest.java |
Tests exclusion of restricted derivatives and source bitstreams. |
dspace-api/src/main/java/org/dspace/discovery/FullTextContentStreams.java |
Filters full-text streams based on Anonymous access policies. |
Review effort: Lite
hasAnonymousRead matched the Anonymous group by equality only, so extracted text whose READ policy is granted through a group that (transitively) contains Anonymous - which also grants anonymous access - stopped being indexed. Accept such a policy via GroupService.isParentOf, matching how DSpace resolves anonymous membership.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Security — exclude restricted-bitstream full text from the Discovery index
Part of dataquest-dev/dspace-customers#1104, see #1083.
Problem
The extracted full text of every bitstream in an item's
TEXTbundle was concatenated into the item's single Solrfulltextfield with no per-bitstream access check, while Discovery access control is applied only at the item level. So for a publicly-readable item that contains an access-restricted file, an anonymous keyword search matched words from the restricted file — a content oracle (probe word-by-word), and the highlighter could even return the restricted snippet.Fix
In
FullTextContentStreams.buildFullTextList(), aTEXT-bundle bitstream is indexed only when it is readable by the Anonymous group — checking both the derivative and its matching sourceORIGINALbitstream (media-filter copies policies, but that copy can drift, so both are verified), with date-valid policies and fail-closed on error. Restricted text therefore never entersfulltext, which closes both the search oracle and the highlight/snippet channel at the source (allsearch_text/ spellcheck /fulltext_hlfields derive fromfulltext). The Anonymous group is resolved once per item for reindex performance.Operational note — reindex required
This affects new indexing only. Existing Solr documents keep previously-indexed restricted text until a full reindex is run on deploy:
dspace index-discovery -b.Intentional trade-off
The gate is anonymous-based on a shared field, so a file readable only by an authenticated group is no longer full-text searchable even for authorized users. Closing the anonymous oracle this way is the minimal, safe fix; per-user full-text search of restricted content would require a larger per-bitstream-field redesign and is out of scope.
Tests
FullTextContentStreamsTest, 8 passing): a restricted derivative is excluded; a readable one is included; and a readable derivative whose source ORIGINAL is restricted is excluded (the policy-drift case).DiscoveryRestControllerIT): public item with a restricted file whose unique token is searched — anonymous gets 0 hits, a token in a public file still returns the item (no regression), admin also gets 0 for the restricted token.🤖 Generated with Claude Code