Skip to content

Fix dspace-customers 1083 R65: escape submitter metadata before rendering it as HTML - #1665

Open
jr-rk wants to merge 5 commits into
dtq-devfrom
fix/r65-search-result-description-text
Open

jr-rk wants to merge 5 commits into
dtq-devfrom
fix/r65-search-result-description-text

Conversation

@jr-rk

@jr-rk jr-rk commented Oct 27, 2026 •

Copy link
Copy Markdown

References

dspace-customers issue 1083, finding R65. Ports upstream DSpace/dspace-angular PR 4881 (the 7.x backport of 4737).

Description

Many components render submitter metadata with [innerHTML], and Angular's sanitizer keeps <a href> and <img src>. So a title, author or description could put links and external images into search results and onto the item page. The metadata is now escaped before it reaches [innerHTML].

Instructions for Reviewers

List of changes in this PR:

  • ea656192b5 is the upstream port. Metadata, DSpaceObject.firstMetadataValue, DSONameService.getName and getHitHighlights take an escapeHTML flag. The result list, grid and detail components escape by default. dc.description in the result list and grid stays [innerHTML], now escaped.
  • Metadata.escapeHighlight: Solr returns hit highlights with the stored value unescaped, so highlights are escaped too and only the <em> tags are kept. Upstream does not escape highlights.
  • makeLinks escapes the text and adds only its own <a> tags. It renders dc.description on the item page and the values on the full item page.
  • dsEmphasize (facet filter suggestions), the autocomplete and sponsor suggestions, and the item name on the request-copy page escape the value.
  • The names of related persons and org units on the item page (virtual metadata) are shown with {{ }}.
  • Values shown with {{ }} pass false, so they are not escaped twice: journal editor and publisher, org unit address, item grid date. Person and org unit name variants stay unescaped, and their input suggestions use {{ }}.
  • Specs for each of these. The specs from the first two commits (a description with <a> and <img> renders as text in the item and entity results) still apply.

A value that really contains HTML, for example <i> in a title, is now shown as text. Upstream behaves the same.

How to test:

yarn run test:headless --include='src/app/core/shared/metadata.utils.spec.ts' --include='src/app/core/breadcrumbs/dso-name.service.spec.ts' --include='src/app/shared/object-list/**/*.spec.ts' --include='src/app/shared/object-grid/**/*.spec.ts' --include='src/app/entity-groups/**/*.spec.ts' --include='src/app/shared/clarin-shared-util.spec.ts' --include='src/app/shared/utils/emphasize.pipe.spec.ts' --include='src/app/item-page/simple/field-components/clarin-description-item-field/*.spec.ts' --include='src/app/shared/form/builder/ds-dynamic-form-ui/models/autocomplete/*.spec.ts'

Result: all specs pass. With the old highlight, makeLinks, dsEmphasize and autocomplete code, the 8 new specs fail.
Manual: set an item's dc.title to Test <img src="https://example.org/x.png"> and its dc.description to <a href="https://example.org">link</a> see https://example.org. Search for Test and open the item. The tags show as text, the search highlight still shows, and the plain URL in the description is still a link.

Not changed, because the HTML there is meant to be HTML or is covered elsewhere:

  • Community and collection intro text, static pages, license texts, the HTML file preview, the opt-in markdown rendering.
  • RefBox citation (finding R68, already sanitized) and CSP headers (R08).

AI Disclosure (REQUIRED)

Were AI tools used in the creation of this PR?

  • No (This PR does NOT include AI-generated or AI-assisted content that requires disclosure.)
  • Yes (This PR includes AI-generated or AI-assisted content that requires disclosure.)

Claude Code ported the upstream change, wrote the CLARIN fixes and the tests. The new specs fail without the escaping and pass with it. Lint passes locally.

Checklist

  • My PR is created against the main branch of code (unless it is a backport or is fixing an issue specific to an older branch).
  • My PR is small in size (e.g. less than 1,000 lines of code, not including comments & integration tests). Exceptions may be made if previously agreed upon.
  • My PR follows all coding best practices based on the Code Conventions Guide.
  • My PR passes ESLint validation using yarn lint
  • My PR doesn't introduce circular dependencies (verified via yarn check-circ-deps)
  • My PR includes TypeDoc comments for all new (or modified) public methods and classes. It also includes TypeDoc for large or complex private methods.
  • My PR passes all specs/tests and includes new/updated specs or tests based on the Code Testing Guide.
  • If my PR includes new libraries/dependencies (in package.json), I've made sure their licenses align with the DSpace BSD License based on the Licensing of Contributions documentation.
  • If my PR includes new features or configurations, I've provided basic technical documentation in the PR itself.
  • If my PR fixes an issue ticket, I've linked them together.

🤖 Generated with Claude Code

…s as text

The item search result list and grid, and the journal, journal volume,
project and org unit result templates, bound the submitter-entered
dc.description with [innerHTML]. Angular's sanitizer drops scripts but
keeps <a href> and <img src>, so a description could inject links and
external images into result listings on the repository origin.

dc.description is not a Solr hit-highlight field, so the value never
carries <em> markup and HTML rendering is not needed. Use text
interpolation instead, as ds-clarin-item-box-view already does.
@jr-rk
jr-rk requested a lite review from Copilot October 27, 2026 13:18
@jr-rk
jr-rk marked this pull request as ready for review October 27, 2026 13:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Add HTML-markup regression coverage for the project, journal, journal-volume, and org-unit result components.

5 open issues
What changed in this PR

This pull request prevents stored dc.description content from being interpreted as HTML in item and entity search results.

Changes:

  • Replaces [innerHTML] with text interpolation across item, project, journal, journal-volume, and org-unit result templates.
  • Adds regression tests for HTML-containing item descriptions in list and grid results.
  • Preserves language attributes and existing truncation behavior.
File Summary
src/​app/​shared/​object-list/​search-result-list-element/​item-search-result/​item-types/​item/​item-search-result-list-element.component.spec.ts Tests that item description markup renders as text in list results.
src/​app/​shared/​object-list/​search-result-list-element/​item-search-result/​item-types/​item/​item-search-result-list-element.component.html Renders item descriptions as text in list results.
src/​app/​shared/​object-grid/​search-result-grid-element/​item-search-result/​item/​item-search-result-grid-element.component.spec.ts Tests that item description markup renders as text in grid results.
src/​app/​shared/​object-grid/​search-result-grid-element/​item-search-result/​item/​item-search-result-grid-element.component.html Renders item descriptions as text in grid results.
src/​app/​entity-groups/​research-entities/​item-list-elements/​search-result-list-elements/​org-unit/​org-unit-search-result-list-element.component.html Renders org-unit descriptions as text.
src/​app/​entity-groups/​research-entities/​item-grid-elements/​search-result-grid-elements/​project/​project-search-result-grid-element.component.html Renders project descriptions as text.
src/​app/​entity-groups/​journal-entities/​item-grid-elements/​search-result-grid-elements/​journal/​journal-search-result-grid-element.component.html Renders journal descriptions as text.
src/​app/​entity-groups/​journal-entities/​item-grid-elements/​search-result-grid-elements/​journal-volume/​journal-volume-search-result-grid-element.component.html Renders journal-volume descriptions as text.

Review effort: Lite

Wire the HTML-in-dc.description fixture into the journal, journal
volume and project grid specs through getEntityGridElementTestComponent,
and add the same check to the org unit list spec, so that reverting any
of those templates to [innerHTML] fails a test.
@jr-rk jr-rk self-assigned this Oct 27, 2026
@jr-rk
jr-rk requested a lite review from Copilot October 27, 2026 14:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The affected templates now safely render descriptions as text, with regression coverage for all reviewed result views.

No open issues 🎉

5 resolved since last review

Review effort: Lite

@jr-rk
jr-rk requested a review from milanmajchrak October 27, 2026 16:08
milanmajchrak and others added 2 commits October 28, 2026 15:04
Port of upstream DSpace/dspace-angular PR 4881 (the 7.x backport of 4737).
The list, grid and detail result components, the name service and the
metadata representations escape metadata values by default. The result
list and grid keep showing dc.description, now escaped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Solr returns hit highlights with the stored value unescaped, so they are
escaped too and only the <em> tags are kept. makeLinks (item page
description, full item page), the facet suggestions, the autocomplete
suggestions and the request-copy item name escape the value as well.
Values shown with {{ }} or kept as name variants opt out, so they are not
escaped twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@milanmajchrak milanmajchrak changed the title Fix dspace-customers 1083 R65: render dc.description in search results as text Fix dspace-customers 1083 R65: escape submitter metadata before rendering it as HTML Oct 28, 2026
The name comes from virtual metadata of the related entity. It has no hit highlights, so it does not need HTML.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

3 participants