diff --git a/projects/aca-content/src/lib/components/search/search-results-row/search-results-row.component.ts b/projects/aca-content/src/lib/components/search/search-results-row/search-results-row.component.ts
index 11c196bfa..5a879e3ad 100644
--- a/projects/aca-content/src/lib/components/search/search-results-row/search-results-row.component.ts
+++ b/projects/aca-content/src/lib/components/search/search-results-row/search-results-row.component.ts
@@ -48,8 +48,20 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export class SearchResultsRowComponent implements OnInit {
private settings = inject(AppSettingsService);
- private readonly highlightPrefix = "";
- private readonly highlightPostfix = '';
+ private readonly highlightPrefix = ``;
+ private readonly highlightPostfix = ``;
+
+ private readonly highlightOpenEscapedRegex = /<span class=(['"])aca-highlight\1>/g;
+ private readonly highlightCloseEscapedRegex = /<\/span>/g;
+
+ private readonly highlightOpenRawRegex = //g;
+ private readonly highlightCloseRawRegex = /<\/span>/g;
+
+ private readonly escapeMap: Record = {
+ '&': '&',
+ '<': '<',
+ '>': '>'
+ };
private node: NodeEntry;
@@ -122,17 +134,26 @@ export class SearchResultsRowComponent implements OnInit {
break;
}
});
- this.name$.next(name);
- this.description$.next(description);
- this.content$.next(content);
+
+ const safeName = this.sanitizeAndHighlight(name);
+ const safeDescription = this.sanitizeAndHighlight(description);
+ const safeContent = this.sanitizeAndHighlight(content);
+
+ this.name$.next(safeName);
+ this.description$.next(safeDescription);
+ this.content$.next(safeContent);
this.nameStripped = this.stripHighlighting(name);
+ this.titleStripped = this.stripHighlighting(title);
this.descriptionStripped = this.stripHighlighting(description);
this.contentStripped = this.stripHighlighting(content);
if (title !== name) {
- this.title$.next(title ? ` ( ${title} )` : '');
+ const safeTitle = this.sanitizeAndHighlight(` ( ${title} )`);
+ this.title$.next(safeTitle);
this.titleStripped = this.stripHighlighting(title);
+ } else {
+ this.title$.next('');
}
}
@@ -149,9 +170,22 @@ export class SearchResultsRowComponent implements OnInit {
this.store.dispatch(new NavigateToFolder(this.node));
}
- private stripHighlighting(highlightedContent: string): string {
- return highlightedContent
- ? highlightedContent.replace(new RegExp(this.highlightPrefix, 'g'), '').replace(new RegExp(this.highlightPostfix, 'g'), '')
- : '';
+ private stripHighlighting(input: string): string {
+ if (!input) {
+ return '';
+ }
+ return input.replace(this.highlightOpenRawRegex, '').replace(this.highlightCloseRawRegex, '');
+ }
+
+ private sanitizeAndHighlight(value: string | null | undefined): string {
+ if (!value) {
+ return '';
+ }
+
+ let escaped = value.replace(/[&<>]/g, (char) => this.escapeMap[char] ?? char);
+
+ escaped = escaped.replace(this.highlightOpenEscapedRegex, this.highlightPrefix).replace(this.highlightCloseEscapedRegex, this.highlightPostfix);
+
+ return escaped;
}
}