Skip to content

feat(config): add option to disable custom html functionality #26956

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

Merged
merged 13 commits into from
Mar 22, 2023
24 changes: 12 additions & 12 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export namespace Components {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down Expand Up @@ -1039,7 +1039,7 @@ export namespace Components {
*/
"loadingSpinner"?: SpinnerTypes | null;
/**
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"loadingText"?: string | IonicSafeString;
}
Expand Down Expand Up @@ -1422,7 +1422,7 @@ export namespace Components {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Optional text content to display in the loading indicator.
* Optional text content to display in the loading indicator. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down Expand Up @@ -2221,15 +2221,15 @@ export namespace Components {
*/
"pullingIcon"?: SpinnerTypes | string | null;
/**
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"pullingText"?: string | IonicSafeString;
/**
* An animated SVG spinner that shows when refreshing begins
*/
"refreshingSpinner"?: SpinnerTypes | null;
/**
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"refreshingText"?: string | IonicSafeString;
}
Expand Down Expand Up @@ -2990,7 +2990,7 @@ export namespace Components {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Message to be shown in the toast.
* Message to be shown in the toast. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down Expand Up @@ -4147,7 +4147,7 @@ declare namespace LocalJSX {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The main message to be displayed in the alert. `message` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down Expand Up @@ -5045,7 +5045,7 @@ declare namespace LocalJSX {
*/
"loadingSpinner"?: SpinnerTypes | null;
/**
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* Optional text to display while loading. `loadingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"loadingText"?: string | IonicSafeString;
}
Expand Down Expand Up @@ -5420,7 +5420,7 @@ declare namespace LocalJSX {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Optional text content to display in the loading indicator.
* Optional text content to display in the loading indicator. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down Expand Up @@ -6188,15 +6188,15 @@ declare namespace LocalJSX {
*/
"pullingIcon"?: SpinnerTypes | string | null;
/**
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when you begin to pull down. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"pullingText"?: string | IonicSafeString;
/**
* An animated SVG spinner that shows when refreshing begins
*/
"refreshingSpinner"?: SpinnerTypes | null;
/**
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
* The text you want to display when performing a refresh. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `<Ionic>` would become `&lt;Ionic&gt;` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"refreshingText"?: string | IonicSafeString;
}
Expand Down Expand Up @@ -6999,7 +6999,7 @@ declare namespace LocalJSX {
*/
"leaveAnimation"?: AnimationBuilder;
/**
* Message to be shown in the toast.
* Message to be shown in the toast. This property accepts custom HTML as a string. Developers who only want to pass plain text can disable the custom HTML functionality by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
"message"?: string | IonicSafeString;
/**
Expand Down
23 changes: 22 additions & 1 deletion core/src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Listen, Method, Prop, Watch, forceUpdate, h } from '@stencil/core';

import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import type {
AlertButton,
Expand All @@ -12,6 +13,7 @@ import type {
OverlayEventDetail,
OverlayInterface,
} from '../../interface';
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
import type { Gesture } from '../../utils/gesture';
import { createButtonActiveGesture } from '../../utils/gesture/button-active';
import { BACKDROP, dismiss, eventMethod, isCancel, prepareOverlay, present, safeCall } from '../../utils/overlays';
Expand Down Expand Up @@ -39,6 +41,7 @@ import { mdLeaveAnimation } from './animations/md.leave';
scoped: true,
})
export class Alert implements ComponentInterface, OverlayInterface {
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);
private activeId?: string;
private inputType?: string;
private processedInputs: AlertInput[] = [];
Expand Down Expand Up @@ -93,6 +96,11 @@ export class Alert implements ComponentInterface, OverlayInterface {
* `&lt;Ionic&gt;`
*
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
*
* This property accepts custom HTML as a string.
* Developers who only want to pass plain text
* can disable the custom HTML functionality
* by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
@Prop() message?: string | IonicSafeString;

Expand Down Expand Up @@ -579,6 +587,19 @@ export class Alert implements ComponentInterface, OverlayInterface {
);
}

private renderAlertMessage(msgId: string) {
const { customHTMLEnabled, message } = this;
if (customHTMLEnabled) {
return <div id={msgId} class="alert-message" innerHTML={sanitizeDOMString(message)}></div>;
}

return (
<div id={msgId} class="alert-message">
{message}
</div>
);
}

render() {
const { overlayIndex, header, subHeader, message, htmlAttributes } = this;
const mode = getIonMode(this);
Expand Down Expand Up @@ -631,7 +652,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
)}
</div>

<div id={msgId} class="alert-message" innerHTML={sanitizeDOMString(message)}></div>
{this.renderAlertMessage(msgId)}

{this.renderAlertInputs()}
{this.renderAlertButtons()}
Expand Down
40 changes: 40 additions & 0 deletions core/src/components/alert/test/alert.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { newSpecPage } from '@stencil/core/testing';
import { Alert } from '../alert';
import { config } from '../../../global/config';

describe('alert: custom html', () => {
it('should allow for custom html by default', async () => {
const page = await newSpecPage({
components: [Alert],
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
});

const content = page.body.querySelector('.alert-message');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});

it('should allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: true });
const page = await newSpecPage({
components: [Alert],
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
});

const content = page.body.querySelector('.alert-message');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});

it('should not allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: false });
const page = await newSpecPage({
components: [Alert],
html: `<ion-alert message="<button class='custom-html'>Custom Text</button>"></ion-alert>`,
});

const content = page.body.querySelector('.alert-message');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).toBe(null);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Component, Host, Prop, h } from '@stencil/core';
import { config } from '../../global/config';
import { getIonMode } from '../../global/ionic-global';
import type { SpinnerTypes } from '../../interface';
import { ENABLE_HTML_CONTENT_DEFAULT } from '../../utils/config';
import type { IonicSafeString } from '../../utils/sanitization';
import { sanitizeDOMString } from '../../utils/sanitization';

Expand All @@ -15,6 +16,8 @@ import { sanitizeDOMString } from '../../utils/sanitization';
},
})
export class InfiniteScrollContent implements ComponentInterface {
private customHTMLEnabled = config.get('innerHTMLTemplatesEnabled', ENABLE_HTML_CONTENT_DEFAULT);

/**
* An animated SVG spinner that shows while loading.
*/
Expand All @@ -28,6 +31,11 @@ export class InfiniteScrollContent implements ComponentInterface {
* `&lt;Ionic&gt;`
*
* For more information: [Security Documentation](https://ionicframework.com/docs/faq/security)
*
* This property accepts custom HTML as a string.
* Developers who only want to pass plain text
* can disable the custom HTML functionality
* by setting `innerHTMLTemplatesEnabled: false` in the Ionic config.
*/
@Prop() loadingText?: string | IonicSafeString;

Expand All @@ -41,6 +49,15 @@ export class InfiniteScrollContent implements ComponentInterface {
}
}

private renderLoadingText() {
const { customHTMLEnabled, loadingText } = this;
if (customHTMLEnabled) {
return <div class="infinite-loading-text" innerHTML={sanitizeDOMString(loadingText)}></div>;
}

return <div class="infinite-loading-text">{this.loadingText}</div>;
}

render() {
const mode = getIonMode(this);
return (
Expand All @@ -58,9 +75,7 @@ export class InfiniteScrollContent implements ComponentInterface {
<ion-spinner name={this.loadingSpinner} />
</div>
)}
{this.loadingText !== undefined && (
<div class="infinite-loading-text" innerHTML={sanitizeDOMString(this.loadingText)} />
)}
{this.loadingText !== undefined && this.renderLoadingText()}
</div>
</Host>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { newSpecPage } from '@stencil/core/testing';
import { InfiniteScrollContent } from '../infinite-scroll-content';
import { config } from '../../../global/config';

describe('infinite-scroll-content: custom html', () => {
it('should allow for custom html by default', async () => {
const page = await newSpecPage({
components: [InfiniteScrollContent],
html: `<ion-infinite-scroll-content loading-text="<button class='custom-html'>Custom Text</button>"></ion-infinite-scroll-content>`,
});

const content = page.body.querySelector('.infinite-loading-text');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});

it('should allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: true });
const page = await newSpecPage({
components: [InfiniteScrollContent],
html: `<ion-infinite-scroll-content loading-text="<button class='custom-html'>Custom Text</button>"></ion-infinite-scroll-content>`,
});

const content = page.body.querySelector('.infinite-loading-text');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).not.toBe(null);
});

it('should not allow for custom html', async () => {
config.reset({ innerHTMLTemplatesEnabled: false });
const page = await newSpecPage({
components: [InfiniteScrollContent],
html: `<ion-infinite-scroll-content loading-text="<button class='custom-html'>Custom Text2</button>"></ion-infinite-scroll-content>`,
});

const content = page.body.querySelector('.infinite-loading-text');
expect(content.textContent).toContain('Custom Text');
expect(content.querySelector('button.custom-html')).toBe(null);
});
});
Loading