diff --git a/src/js/Item.ts b/src/js/Item.ts index d211d9a..a926712 100644 --- a/src/js/Item.ts +++ b/src/js/Item.ts @@ -1,4 +1,5 @@ import {ModelAttributes} from './galleries/AbstractGallery'; +import {sanitizeHtml} from "./Utility"; export declare interface ItemOptions { lightbox?: boolean; @@ -8,12 +9,6 @@ export declare interface ItemOptions { showLabels?: 'hover' | 'never' | 'always'; } -export interface ItemTitle { - title: string; - link: string; - linkTarget: '_blank' | '_self' | '_parent' | '_top'; -} - export type ItemActivateEventDetail = { clickEvent: MouseEvent; item: Item; @@ -72,7 +67,6 @@ export class Item { /** * - * @param {ItemOptions} options * @param model Contains the source data given for an item (e.g object instance from database with id etc..) */ public constructor( @@ -80,16 +74,7 @@ export class Item { private readonly options: ItemOptions, public readonly model: Model, ) { - this.title = this.getTitleDetails(model.title); - } - - /** - * Cleans html, and returns only the text from all eventual tags - * @param {string} term - * @returns {ItemTitle} - */ - private getTitleDetails(term: string | undefined): string { - return term ? term.replace(/<(?!\s*br\s*\/?)[^>]+>/gi, '') : ''; + this.title = sanitizeHtml(model.title); } /** diff --git a/src/js/Utility.ts b/src/js/Utility.ts index 7aa3af8..27284bc 100644 --- a/src/js/Utility.ts +++ b/src/js/Utility.ts @@ -51,3 +51,10 @@ export function getImageRatioAndIfCropped(model: SizedModel, ratioLimits?: Ratio return {ratio: ratio, cropped: cropped}; } + +/** + * Cleans HTML, and returns only the plain text and `
` from all eventual tags + */ +export function sanitizeHtml(term: string | undefined): string { + return term ? term.replace(/<(?!\s*br\s*\/?)[^>]*>/gi, '') : ''; +} diff --git a/testing/unit/utility.spec.ts b/testing/unit/utility.spec.ts index 6540ff8..e89052b 100644 --- a/testing/unit/utility.spec.ts +++ b/testing/unit/utility.spec.ts @@ -1,4 +1,4 @@ -import {getIcon, getImageRatio} from '../../src/js/Utility'; +import {getIcon, getImageRatio, sanitizeHtml} from '../../src/js/Utility'; import * as domino from 'domino'; describe('Utility', () => { @@ -26,3 +26,40 @@ describe('Utility', () => { expect(svg.outerHTML).toBe(''); }); }); + +describe('sanitizeHtml', () => { + + test('should not touch plain text and
', () => { + expect(sanitizeHtml('')).toBe(''); + expect(sanitizeHtml(' ')).toBe(' '); + expect(sanitizeHtml('foo')).toBe('foo'); + expect(sanitizeHtml('one > two > three')).toBe('one > two > three'); + expect(sanitizeHtml('foo
bar')).toBe('foo
bar'); + expect(sanitizeHtml('foo
bar')).toBe('foo
bar'); + expect(sanitizeHtml('foo
bar')).toBe('foo
bar'); + expect(sanitizeHtml('foo
bar')).toBe('foo
bar'); + expect(sanitizeHtml('foo
bar')).toBe('foo
bar'); + }); + + test('should remove most HTML tag', () => { + expect(sanitizeHtml('')).toBe(''); + expect(sanitizeHtml('')).toBe(''); + expect(sanitizeHtml('foo bar baz')).toBe('foo bar baz'); + expect(sanitizeHtml('foo bar baz')).toBe('foo bar baz'); + expect(sanitizeHtml('foo bar baz')).toBe('foo bar baz'); + expect(sanitizeHtml('foo bar baz')).toBe('foo bar baz'); + expect(sanitizeHtml('foo \nbar\n \nbaz')).toBe('foo \nbar\n \nbaz'); + expect(sanitizeHtml('foo bar baz')).toBe('foo bar baz'); + expect(sanitizeHtml('one > two > three')).toBe('one > two > three'); + expect(sanitizeHtml('is removedt>alert(123)')).toBe('is removedt>alert(123)'); // Broken but safe HTML + expect(sanitizeHtml('>')).toBe('>'); // Broken but safe HTML + expect(sanitizeHtml('a<>b')).toBe('ab'); + expect(sanitizeHtml('ab')).toBe('ab'); + expect(sanitizeHtml('a<>bc')).toBe('abc'); + expect( + sanitizeHtml(`
foo`), + ).toBe('foo'); + }); +});