diff --git a/.changeset/famous-cameras-kiss.md b/.changeset/famous-cameras-kiss.md new file mode 100644 index 0000000000..eb48b02f2a --- /dev/null +++ b/.changeset/famous-cameras-kiss.md @@ -0,0 +1,5 @@ +--- +'@swisspost/internet-header': patch +--- + +Sanitized hours fields in footer against XSS "Incomplete multi-character sanitization" issue. diff --git a/packages/internet-header/cypress/e2e/footer.cy.ts b/packages/internet-header/cypress/e2e/footer.cy.ts index 618159dc41..83afed2159 100644 --- a/packages/internet-header/cypress/e2e/footer.cy.ts +++ b/packages/internet-header/cypress/e2e/footer.cy.ts @@ -54,5 +54,23 @@ describe('footer', () => { }); }); }); + + describe('block-contact', () => { + it('should display pure (without HTML) hours content as it is', () => { + prepare(FOOTER, 'Default'); + cy.get('.block-contact .content-row .text') + .contains('Saturday') + .siblings('.hours') + .should('contain.text', '8am to 12 noon'); + }); + + it('should remove wrapping HTML in hours content when value contains HTML', () => { + prepare(FOOTER, 'Default'); + cy.get('.block-contact .content-row .text') + .contains('Bank holidays') + .siblings('.hours') + .should('contain.text', '8—12'); + }); + }); }); }); diff --git a/packages/internet-header/cypress/fixtures/internet-header/test-configuration.json b/packages/internet-header/cypress/fixtures/internet-header/test-configuration.json index 65e5f7a3f2..ab95befce8 100644 --- a/packages/internet-header/cypress/fixtures/internet-header/test-configuration.json +++ b/packages/internet-header/cypress/fixtures/internet-header/test-configuration.json @@ -3046,6 +3046,16 @@ "text": "Saturday", "title": null }, + { + "address": null, + "describe": null, + "hours": "

8—12

", + "links": null, + "name": "days", + "number": null, + "text": "Bank holidays", + "title": null + }, { "address": null, "describe": null, diff --git a/packages/internet-header/src/components/post-internet-footer/components/post-footer-block-contact.component.tsx b/packages/internet-header/src/components/post-internet-footer/components/post-footer-block-contact.component.tsx index 1dbe1e50eb..988394ec37 100644 --- a/packages/internet-header/src/components/post-internet-footer/components/post-footer-block-contact.component.tsx +++ b/packages/internet-header/src/components/post-internet-footer/components/post-footer-block-contact.component.tsx @@ -1,8 +1,10 @@ import { h } from '@stencil/core'; import { BlockEntity } from '../../../models/footer.model'; -const getContentHours = (hours: string) => hours.replace(/<[^>]*>?/gm, ''); - +function stripHtml(html: string): string { + const doc = new DOMParser().parseFromString(html, 'text/html'); + return doc.body.textContent || ''; +} const callUnblu = () => { if (typeof window['unbluLSLoad'] === 'function') { window['unbluLSLoad'](); @@ -19,7 +21,7 @@ const LiveSupport = (props: { hours: string }) => ( id="liveSupport" type="button" onClick={callUnblu} - innerHTML={getContentHours(props.hours)} + innerHTML={stripHtml(props.hours)} > ); @@ -44,8 +46,8 @@ export const PostFooterBlockContact = (props: { {content.text ?

{content.text}

: null} {content.hours && isLiveSupport && } {content.hours && !isLiveSupport && ( - // Some values arrive in the form of

8&emdash;12

and without replace and innerHTML, tags get rendered as text (project="klp" language="en" environment="int02") -

+ // Some values arrive in the form of

8—12

and without replace and innerHTML, tags get rendered as text (project="klp" language="en" environment="int02") +

)} {content.describe ?

{content.describe}

: null}