Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(internet-header): Incomplete multi-character sanitization Code Scanning alert on hours field of internet-header footer #2807

Merged
merged 4 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/famous-cameras-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@swisspost/internet-header': patch
---

Sanitized hours fields in footer against XSS "Incomplete multi-character sanitization" issue.
18 changes: 18 additions & 0 deletions packages/internet-header/cypress/e2e/footer.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3046,6 +3046,16 @@
"text": "Saturday",
"title": null
},
{
"address": null,
"describe": null,
"hours": "<p>8&mdash;12</p>",
"links": null,
"name": "days",
"number": null,
"text": "Bank holidays",
"title": null
},
{
"address": null,
"describe": null,
Expand Down
Original file line number Diff line number Diff line change
@@ -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');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

DOMParser().parseFromString is not executing any script. It can still contain malicious code like onclick event that will work if it's not santitize inside the DOMParser and that the result is injected bad in the current DOM document. In our case, it's safe because using "textContent" will always allow only text. Also, this technic is used by DOMPurify library.

More infos:

return doc.body.textContent || '';
}
const callUnblu = () => {
if (typeof window['unbluLSLoad'] === 'function') {
window['unbluLSLoad']();
Expand All @@ -14,13 +16,9 @@ const callUnblu = () => {
};

const LiveSupport = (props: { hours: string }) => (
<button
class="hours btn btn-link"
id="liveSupport"
type="button"
onClick={callUnblu}
innerHTML={getContentHours(props.hours)}
></button>
<button class="hours btn btn-link" id="liveSupport" type="button" onClick={callUnblu}>
{stripHtml(props.hours)}
</button>
);

export const PostFooterBlockContact = (props: {
Expand All @@ -44,8 +42,8 @@ export const PostFooterBlockContact = (props: {
{content.text ? <p class="text">{content.text}</p> : null}
{content.hours && isLiveSupport && <LiveSupport hours={content.hours} />}
{content.hours && !isLiveSupport && (
// Some values arrive in the form of <p>8&emdash;12</p> and without replace and innerHTML, tags get rendered as text (project="klp" language="en" environment="int02")
<p class="hours" innerHTML={getContentHours(content.hours)}></p>
// Some values arrive in the form of <p>8&mdash;12</p> and without replace and innerHTML, tags get rendered as text (project="klp" language="en" environment="int02")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

&emdash; is not decoded with any library. It seems to never have existed (at least officially), see: https://bugzilla.mozilla.org/show_bug.cgi?id=221606

<p class="hours">{stripHtml(content.hours)}</p>
)}
{content.describe ? <p class="describe">{content.describe}</p> : null}
</div>
Expand Down
Loading