From bf38ae98f1ac3bae2c6006888a8705e42fbb804f Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 18 Dec 2025 06:31:25 -0800 Subject: [PATCH] Security: remove safe html pipe --- .../admin/settings/settings.component.spec.ts | 2 -- .../admin/trash/trash.component.spec.ts | 2 -- .../confirm-dialog.component.html | 2 +- .../confirm-dialog.component.spec.ts | 5 ++-- .../confirm-dialog.component.ts | 3 +-- .../rotate-confirm-dialog.component.html | 4 ++-- .../rotate-confirm-dialog.component.spec.ts | 3 --- .../rotate-confirm-dialog.component.ts | 3 +-- ...custom-field-edit-dialog.component.spec.ts | 2 -- .../mail-rule-edit-dialog.component.spec.ts | 2 -- .../workflow-edit-dialog.component.spec.ts | 2 -- .../input/entries/entries.component.html | 2 +- .../common/input/file/file.component.html | 2 +- .../input/password/password.component.html | 2 +- .../common/input/text/text.component.html | 2 +- .../common/input/text/text.component.ts | 8 +------ .../input/textarea/textarea.component.html | 2 +- .../input/textarea/textarea.component.ts | 8 +------ .../common/input/url/url.component.html | 2 +- .../permissions-dialog.component.spec.ts | 2 -- .../profile-edit-dialog.component.html | 4 +++- .../profile-edit-dialog.component.ts | 9 +++++-- .../document-list.component.spec.ts | 2 -- .../manage/mail/mail.component.spec.ts | 2 -- .../management-list.component.html | 10 ++++++-- .../management-list.component.spec.ts | 2 -- .../management-list.component.ts | 8 +++++-- .../storage-path-list.component.spec.ts | 2 -- .../storage-path-list.component.ts | 2 -- .../tag-list/tag-list.component.spec.ts | 2 -- .../manage/tag-list/tag-list.component.ts | 11 ++++----- src-ui/src/app/pipes/safehtml.pipe.spec.ts | 24 ------------------- src-ui/src/app/pipes/safehtml.pipe.ts | 13 ---------- 33 files changed, 44 insertions(+), 107 deletions(-) delete mode 100644 src-ui/src/app/pipes/safehtml.pipe.spec.ts delete mode 100644 src-ui/src/app/pipes/safehtml.pipe.ts diff --git a/src-ui/src/app/components/admin/settings/settings.component.spec.ts b/src-ui/src/app/components/admin/settings/settings.component.spec.ts index 300067d1b..cc5c96640 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.spec.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.spec.ts @@ -28,7 +28,6 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { PermissionsGuard } from 'src/app/guards/permissions.guard' import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { PermissionsService } from 'src/app/services/permissions.service' import { GroupService } from 'src/app/services/rest/group.service' import { SavedViewService } from 'src/app/services/rest/saved-view.service' @@ -129,7 +128,6 @@ describe('SettingsComponent', () => { ConfirmDialogComponent, CheckComponent, ColorComponent, - SafeHtmlPipe, SelectComponent, TextComponent, NumberComponent, diff --git a/src-ui/src/app/components/admin/trash/trash.component.spec.ts b/src-ui/src/app/components/admin/trash/trash.component.spec.ts index aa5a8af0f..215b0b253 100644 --- a/src-ui/src/app/components/admin/trash/trash.component.spec.ts +++ b/src-ui/src/app/components/admin/trash/trash.component.spec.ts @@ -11,7 +11,6 @@ import { } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { of, throwError } from 'rxjs' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { ToastService } from 'src/app/services/toast.service' import { TrashService } from 'src/app/services/trash.service' import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component' @@ -53,7 +52,6 @@ describe('TrashComponent', () => { TrashComponent, PageHeaderComponent, ConfirmDialogComponent, - SafeHtmlPipe, ], }).compileComponents() diff --git a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html index e25ace612..deee54402 100644 --- a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html +++ b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html @@ -8,7 +8,7 @@

{{messageBold}}

} @if (message) { -

+

} diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts index 55cbf152a..e59671921 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts @@ -5,7 +5,6 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap' import { NgSelectModule } from '@ng-select/ng-select' import { of } from 'rxjs' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { UserService } from 'src/app/services/rest/user.service' import { PermissionsFormComponent } from '../input/permissions/permissions-form/permissions-form.component' import { PermissionsGroupComponent } from '../input/permissions/permissions-group/permissions-group.component' @@ -41,7 +40,6 @@ describe('PermissionsDialogComponent', () => { ReactiveFormsModule, NgbModule, PermissionsDialogComponent, - SafeHtmlPipe, SelectComponent, SwitchComponent, PermissionsFormComponent, diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html index a8627f19c..67098b55c 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html @@ -110,7 +110,9 @@
Loading...
} @else if (totpSettings) {
-
+ @if (qrSvgDataUrl) { + Authenticator QR code + }
Scan the QR code with your authenticator app and then enter the code below

diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts index c4a103397..d6c839f1e 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts @@ -18,7 +18,6 @@ import { SocialAccountProvider, TotpSettings, } from 'src/app/data/user-profile' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { ProfileService } from 'src/app/services/profile.service' import { ToastService } from 'src/app/services/toast.service' import { setLocationHref } from 'src/app/utils/navigation' @@ -37,7 +36,6 @@ import { TextComponent } from '../input/text/text.component' PasswordComponent, FormsModule, ReactiveFormsModule, - SafeHtmlPipe, NgbAccordionModule, NgbPopoverModule, NgxBootstrapIconsModule, @@ -89,6 +87,13 @@ export class ProfileEditDialogComponent public socialAccounts: SocialAccount[] = [] public socialAccountProviders: SocialAccountProvider[] = [] + get qrSvgDataUrl(): string | null { + if (!this.totpSettings?.qr_svg) { + return null + } + return `data:image/svg+xml;utf8,${encodeURIComponent(this.totpSettings.qr_svg)}` + } + ngOnInit(): void { this.networkActive = true this.profileService diff --git a/src-ui/src/app/components/document-list/document-list.component.spec.ts b/src-ui/src/app/components/document-list/document-list.component.spec.ts index a64d79e49..f39225f3f 100644 --- a/src-ui/src/app/components/document-list/document-list.component.spec.ts +++ b/src-ui/src/app/components/document-list/document-list.component.spec.ts @@ -36,7 +36,6 @@ import { PermissionsGuard } from 'src/app/guards/permissions.guard' import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe' import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe' import { FilterPipe } from 'src/app/pipes/filter.pipe' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { UsernamePipe } from 'src/app/pipes/username.pipe' import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { PermissionsService } from 'src/app/services/permissions.service' @@ -103,7 +102,6 @@ describe('DocumentListComponent', () => { DatePipe, DocumentTitlePipe, UsernamePipe, - SafeHtmlPipe, PermissionsGuard, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), diff --git a/src-ui/src/app/components/manage/mail/mail.component.spec.ts b/src-ui/src/app/components/manage/mail/mail.component.spec.ts index 38293aca9..c866160d4 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.spec.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.spec.ts @@ -23,7 +23,6 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { PermissionsGuard } from 'src/app/guards/permissions.guard' import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { PermissionsService } from 'src/app/services/permissions.service' import { MailAccountService } from 'src/app/services/rest/mail-account.service' import { MailRuleService } from 'src/app/services/rest/mail-rule.service' @@ -84,7 +83,6 @@ describe('MailComponent', () => { CustomDatePipe, ConfirmDialogComponent, CheckComponent, - SafeHtmlPipe, SelectComponent, TextComponent, PasswordComponent, diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.html b/src-ui/src/app/components/manage/management-list/management-list.component.html index 23e518f31..8fac6f44f 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.html +++ b/src-ui/src/app/components/manage/management-list/management-list.component.html @@ -94,8 +94,14 @@ {{ getDocumentCount(object) }} @for (column of extraColumns; track column) { - @if (column.rendersHtml) { -

+ @if (column.badgeFn) { + + {{ column.badgeFn.call(null, object)?.text }} + } @else if (column.monospace) { {{ column.valueFn.call(null, object) }} } @else { diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts index 9c64f5730..813c81148 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts @@ -33,7 +33,6 @@ import { Tag } from 'src/app/data/tag' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { SortableDirective } from 'src/app/directives/sortable.directive' import { PermissionsGuard } from 'src/app/guards/permissions.guard' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { PermissionAction, @@ -93,7 +92,6 @@ describe('ManagementListComponent', () => { SortableDirective, PageHeaderComponent, IfPermissionsDirective, - SafeHtmlPipe, ConfirmDialogComponent, PermissionsDialogComponent, ], diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.ts b/src-ui/src/app/components/manage/management-list/management-list.component.ts index 7cc3eaf4b..b1af1f1d1 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.ts @@ -48,9 +48,13 @@ export interface ManagementListColumn { name: string - valueFn: any + valueFn?: any - rendersHtml?: boolean + badgeFn?: (object: any) => { + text: string + textColor?: string + backgroundColor?: string + } hideOnMobile?: boolean diff --git a/src-ui/src/app/components/manage/storage-path-list/storage-path-list.component.spec.ts b/src-ui/src/app/components/manage/storage-path-list/storage-path-list.component.spec.ts index 22060bc61..e59c52789 100644 --- a/src-ui/src/app/components/manage/storage-path-list/storage-path-list.component.spec.ts +++ b/src-ui/src/app/components/manage/storage-path-list/storage-path-list.component.spec.ts @@ -9,7 +9,6 @@ import { of } from 'rxjs' import { StoragePath } from 'src/app/data/storage-path' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { SortableDirective } from 'src/app/directives/sortable.directive' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { StoragePathService } from 'src/app/services/rest/storage-path.service' import { PageHeaderComponent } from '../../common/page-header/page-header.component' import { StoragePathListComponent } from './storage-path-list.component' @@ -30,7 +29,6 @@ describe('StoragePathListComponent', () => { SortableDirective, PageHeaderComponent, IfPermissionsDirective, - SafeHtmlPipe, ], providers: [ DatePipe, diff --git a/src-ui/src/app/components/manage/storage-path-list/storage-path-list.component.ts b/src-ui/src/app/components/manage/storage-path-list/storage-path-list.component.ts index 346d956e8..413ccc33a 100644 --- a/src-ui/src/app/components/manage/storage-path-list/storage-path-list.component.ts +++ b/src-ui/src/app/components/manage/storage-path-list/storage-path-list.component.ts @@ -10,7 +10,6 @@ import { FILTER_HAS_STORAGE_PATH_ANY } from 'src/app/data/filter-rule-type' import { StoragePath } from 'src/app/data/storage-path' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { SortableDirective } from 'src/app/directives/sortable.directive' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { PermissionType } from 'src/app/services/permissions.service' import { StoragePathService } from 'src/app/services/rest/storage-path.service' import { StoragePathEditDialogComponent } from '../../common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component' @@ -26,7 +25,6 @@ import { ManagementListComponent } from '../management-list/management-list.comp PageHeaderComponent, TitleCasePipe, IfPermissionsDirective, - SafeHtmlPipe, FormsModule, ReactiveFormsModule, NgClass, diff --git a/src-ui/src/app/components/manage/tag-list/tag-list.component.spec.ts b/src-ui/src/app/components/manage/tag-list/tag-list.component.spec.ts index 0db84182b..9b1923e43 100644 --- a/src-ui/src/app/components/manage/tag-list/tag-list.component.spec.ts +++ b/src-ui/src/app/components/manage/tag-list/tag-list.component.spec.ts @@ -8,7 +8,6 @@ import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { of } from 'rxjs' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { SortableDirective } from 'src/app/directives/sortable.directive' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { TagService } from 'src/app/services/rest/tag.service' import { PageHeaderComponent } from '../../common/page-header/page-header.component' import { TagListComponent } from './tag-list.component' @@ -30,7 +29,6 @@ describe('TagListComponent', () => { SortableDirective, PageHeaderComponent, IfPermissionsDirective, - SafeHtmlPipe, ], providers: [ DatePipe, diff --git a/src-ui/src/app/components/manage/tag-list/tag-list.component.ts b/src-ui/src/app/components/manage/tag-list/tag-list.component.ts index 64ca121df..0ba0a0855 100644 --- a/src-ui/src/app/components/manage/tag-list/tag-list.component.ts +++ b/src-ui/src/app/components/manage/tag-list/tag-list.component.ts @@ -10,7 +10,6 @@ import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type' import { Tag } from 'src/app/data/tag' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { SortableDirective } from 'src/app/directives/sortable.directive' -import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe' import { PermissionType } from 'src/app/services/permissions.service' import { TagService } from 'src/app/services/rest/tag.service' import { TagEditDialogComponent } from '../../common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component' @@ -26,7 +25,6 @@ import { ManagementListComponent } from '../management-list/management-list.comp PageHeaderComponent, TitleCasePipe, IfPermissionsDirective, - SafeHtmlPipe, FormsModule, ReactiveFormsModule, NgClass, @@ -49,10 +47,11 @@ export class TagListComponent extends ManagementListComponent { { key: 'color', name: $localize`Color`, - rendersHtml: true, - valueFn: (t: Tag) => { - return `${t.color}` - }, + badgeFn: (t: Tag) => ({ + text: t.color, + textColor: t.text_color, + backgroundColor: t.color, + }), }, ] } diff --git a/src-ui/src/app/pipes/safehtml.pipe.spec.ts b/src-ui/src/app/pipes/safehtml.pipe.spec.ts deleted file mode 100644 index 8ef9a9e47..000000000 --- a/src-ui/src/app/pipes/safehtml.pipe.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { TestBed } from '@angular/core/testing' -import { BrowserModule, DomSanitizer } from '@angular/platform-browser' -import { SafeHtmlPipe } from './safehtml.pipe' - -describe('SafeHtmlPipe', () => { - let pipe: SafeHtmlPipe - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [SafeHtmlPipe], - imports: [BrowserModule], - }) - pipe = TestBed.inject(SafeHtmlPipe) - }) - - it('should bypass security and trust the url', () => { - const html = '
some content
' - const domSanitizer = TestBed.inject(DomSanitizer) - const sanitizerSpy = jest.spyOn(domSanitizer, 'bypassSecurityTrustHtml') - let safeHtml = pipe.transform(html) - expect(safeHtml).not.toBeNull() - expect(sanitizerSpy).toHaveBeenCalled() - }) -}) diff --git a/src-ui/src/app/pipes/safehtml.pipe.ts b/src-ui/src/app/pipes/safehtml.pipe.ts deleted file mode 100644 index 1001b8dbf..000000000 --- a/src-ui/src/app/pipes/safehtml.pipe.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Pipe, PipeTransform, inject } from '@angular/core' -import { DomSanitizer } from '@angular/platform-browser' - -@Pipe({ - name: 'safeHtml', -}) -export class SafeHtmlPipe implements PipeTransform { - private sanitizer = inject(DomSanitizer) - - transform(html) { - return this.sanitizer.bypassSecurityTrustHtml(html) - } -}