From 99c7fa9836d5387450a6e82cbfcf3ffab0131457 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Fri, 19 Apr 2019 15:25:45 -0400 Subject: [PATCH 01/21] Add sanitization method and tests --- core/src/utils/sanitization/index.ts | 31 ++++++++ core/src/utils/sanitization/test/e2e.ts | 22 ++++++ core/src/utils/sanitization/test/index.html | 87 +++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 core/src/utils/sanitization/index.ts create mode 100644 core/src/utils/sanitization/test/e2e.ts create mode 100644 core/src/utils/sanitization/test/index.html diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts new file mode 100644 index 00000000000..e0e4aca5dd4 --- /dev/null +++ b/core/src/utils/sanitization/index.ts @@ -0,0 +1,31 @@ +/** + * Removes any 'on' event handlers + * strips javascript from 'href' props + */ +export const sanitizeDOMString = (untrustedString: string): string => { + const whitelistedAttribs = ['class', 'id', 'href', 'src']; + const range = document.createRange(); + const documentFragment = range.createContextualFragment(untrustedString); + + Array.from(documentFragment.children).forEach(childEl => { + for (const attributeName of childEl.getAttributeNames()) { + + // remove non-whitelisted attribs + if (!whitelistedAttribs.includes(attributeName.toLowerCase())) { + childEl.removeAttribute(attributeName); + continue; + } + + // clean up any whitelisted attribs + // that attempt to do any JS funny-business + const attributeValue = childEl.getAttribute(attributeName); + + /* tslint:disable-next-line */ + if (attributeValue !== null && attributeValue.toLowerCase().includes('javascript:')) { + childEl.removeAttribute(attributeName); + } + } + }); + + return new XMLSerializer().serializeToString(documentFragment); +}; diff --git a/core/src/utils/sanitization/test/e2e.ts b/core/src/utils/sanitization/test/e2e.ts new file mode 100644 index 00000000000..106292ac8ba --- /dev/null +++ b/core/src/utils/sanitization/test/e2e.ts @@ -0,0 +1,22 @@ +import { newE2EPage } from '@stencil/core/testing'; + +test('sanitization:', async done => { + + const page = await newE2EPage({ + url: '/src/utils/sanitization/test?ionic:_testing=true' + }); + + page.on('pageerror', (err: any) => { + if (err.message.includes('sanitizeFailed')) { + done.fail(new Error('Failed to properly sanitize')); + } + }); + + await page.click('#testA'); + await page.click('#testB'); + await page.click('#testC'); + await page.click('#testD'); + + done(); + +}); diff --git a/core/src/utils/sanitization/test/index.html b/core/src/utils/sanitization/test/index.html new file mode 100644 index 00000000000..038ba300b86 --- /dev/null +++ b/core/src/utils/sanitization/test/index.html @@ -0,0 +1,87 @@ + + + + + Sanitization + + + + + + + + + + + + + + + + Sanitization + + + + +
Results will appear here
+ + Test A + Test B + Test C + Test D +
+ +
+ + + + + From 89557229fc81213a1f0e6fa34c1e47304acf8953 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Fri, 19 Apr 2019 15:58:13 -0400 Subject: [PATCH 02/21] integrate sanitizer with innerHTML usage --- core/src/components/alert/alert.tsx | 3 ++- core/src/components/alert/test/basic/index.html | 2 +- .../infinite-scroll-content.tsx | 3 ++- .../refresher-content/refresher-content.tsx | 5 +++-- core/src/components/searchbar/searchbar.tsx | 3 ++- core/src/utils/sanitization/index.ts | 13 ++++++++----- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index d3d0937f029..52cb2f8e424 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -2,6 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me import { AlertButton, AlertInput, Animation, AnimationBuilder, Config, CssClassMap, Mode, OverlayEventDetail, OverlayInterface } from '../../interface'; import { BACKDROP, dismiss, eventMethod, isCancel, present } from '../../utils/overlays'; +import { sanitizeDOMString } from '../../utils/sanitization'; import { getClassMap } from '../../utils/theme'; import { iosEnterAnimation } from './animations/ios.enter'; @@ -440,7 +441,7 @@ export class Alert implements ComponentInterface, OverlayInterface { {this.subHeader &&

{this.subHeader}

} -
+
{this.renderAlertInputs(labelledById)} {this.renderAlertButtons()} diff --git a/core/src/components/alert/test/basic/index.html b/core/src/components/alert/test/basic/index.html index 5d1fe8e0a3a..57b6464a9ed 100644 --- a/core/src/components/alert/test/basic/index.html +++ b/core/src/components/alert/test/basic/index.html @@ -66,7 +66,7 @@ await alertController.componentOnReady(); const alert = await alertController.create({ header: 'Alert', - message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum hendrerit diam lorem, a faucibus turpis sagittis eu. In finibus augue in dui varius convallis. Donec vulputate nibh gravida odio vulputate commodo. Suspendisse imperdiet consequat egestas. Nulla feugiat consequat urna eu tincidunt. Cras nec blandit turpis, eu auctor nunc. Pellentesque finibus, magna eu vestibulum imperdiet, arcu ex lacinia massa, eget volutpat quam leo a orci. Etiam mauris est, elementum at feugiat at, dictum in sapien. Mauris efficitur eros sodales convallis egestas. Phasellus eu faucibus nisl. In eu diam vitae libero egestas lacinia. Integer sed convallis metus, nec commodo felis. Duis libero augue, ornare at tempus non, posuere vel augue. Cras mattis dui at tristique aliquam. Phasellus fermentum nibh ligula, porta hendrerit ligula elementum eu. Suspendisse sollicitudin enim at libero iaculis pulvinar. Donec ac massa id purus laoreet rutrum quis eu urna. Mauris luctus erat vel magna porttitor, vel varius erat rhoncus. Donec eu turpis vestibulum, feugiat urna id, gravida mauris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer at lobortis tortor. Nam ultrices volutpat elit, sed pharetra nulla suscipit at. Nunc eu accumsan eros, id auctor libero. Suspendisse potenti. Nam vitae dapibus metus. Maecenas nisi dui, sagittis et condimentum eu, bibendum vel eros. Vivamus malesuada, tortor in accumsan iaculis, urna velit consectetur ante, nec semper sem diam a diam. In et semper ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus blandit, velit vel porttitor euismod, neque risus blandit nulla, non laoreet libero dolor et odio. Nulla enim risus, feugiat eu urna sed, ultrices semper felis. Sed blandit mi diam. Nunc quis mi ligula. Pellentesque a elit eu orci volutpat egestas. Aenean fermentum eleifend quam, ut tincidunt eros tristique et. Nam dapibus tincidunt ligula, id faucibus felis sodales quis. Donec tincidunt lectus ipsum, ac semper tellus cursus ac. Vestibulum nec dui a lectus accumsan vestibulum quis et velit. Aliquam finibus justo et odio euismod, viverra condimentum eros tristique. Sed eget luctus risus. Pellentesque lorem magna, dictum non congue sodales, laoreet eget quam. In sagittis vulputate dolor a ultricies. Donec viverra leo sed ex maximus, in finibus elit gravida. Aliquam posuere vulputate mi. Suspendisse potenti. Nunc consectetur congue arcu, at pharetra dui varius non. Etiam vestibulum congue felis, id ullamcorper neque convallis ultrices. Aenean congue, diam a iaculis mollis, nisl eros maximus arcu, nec hendrerit purus felis porta diam. Nullam vitae ultrices dui, ac dictum sapien. Phasellus eu magna luctus, varius urna id, molestie quam. Nulla in semper tellus. Curabitur lacinia tellus sit amet lacinia dapibus. Sed id condimentum tellus, nec aliquam sapien. Vivamus luctus at ante a tincidunt.', + message: ' )} {this.loadingText && ( -
+
)}
); diff --git a/core/src/components/refresher-content/refresher-content.tsx b/core/src/components/refresher-content/refresher-content.tsx index 2cbbd3ff17f..218f07eca6f 100644 --- a/core/src/components/refresher-content/refresher-content.tsx +++ b/core/src/components/refresher-content/refresher-content.tsx @@ -1,6 +1,7 @@ import { Component, ComponentInterface, Prop } from '@stencil/core'; import { Config, Mode, SpinnerTypes } from '../../interface'; +import { sanitizeDOMString } from '../../utils/sanitization'; @Component({ tag: 'ion-refresher-content' @@ -60,7 +61,7 @@ export class RefresherContent implements ComponentInterface {
} {this.pullingText && -
+
} ,
@@ -70,7 +71,7 @@ export class RefresherContent implements ComponentInterface {
} {this.refreshingText && -
+
} ]; diff --git a/core/src/components/searchbar/searchbar.tsx b/core/src/components/searchbar/searchbar.tsx index 1645a2b2aaf..931083786f8 100644 --- a/core/src/components/searchbar/searchbar.tsx +++ b/core/src/components/searchbar/searchbar.tsx @@ -2,6 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Pr import { Color, Config, Mode, SearchbarChangeEventDetail } from '../../interface'; import { debounceEvent } from '../../utils/helpers'; +import { sanitizeDOMString } from '../../utils/sanitization'; import { createColorClasses } from '../../utils/theme'; @Component({ @@ -288,7 +289,7 @@ export class Searchbar implements ComponentInterface { // Create a dummy span to get the placeholder width const doc = this.doc; const tempSpan = doc.createElement('span'); - tempSpan.innerHTML = this.placeholder; + tempSpan.innerHTML = sanitizeDOMString(this.placeholder) || ''; doc.body.appendChild(tempSpan); // Get the width of the span then remove it diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index e0e4aca5dd4..7a5ecc7f158 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -1,13 +1,16 @@ /** - * Removes any 'on' event handlers - * strips javascript from 'href' props + * Does a simple sanitization of all elements + * in an untrusted string */ -export const sanitizeDOMString = (untrustedString: string): string => { + +export const sanitizeDOMString = (untrustedString: string | undefined): string | undefined => { + if (untrustedString === undefined) { return untrustedString; } + const whitelistedAttribs = ['class', 'id', 'href', 'src']; const range = document.createRange(); const documentFragment = range.createContextualFragment(untrustedString); - Array.from(documentFragment.children).forEach(childEl => { + for (const childEl of (documentFragment.children as any)) { for (const attributeName of childEl.getAttributeNames()) { // remove non-whitelisted attribs @@ -25,7 +28,7 @@ export const sanitizeDOMString = (untrustedString: string): string => { childEl.removeAttribute(attributeName); } } - }); + } return new XMLSerializer().serializeToString(documentFragment); }; From 4869f825848574491be8f5167b4c3419d89cf298 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Fri, 19 Apr 2019 15:59:21 -0400 Subject: [PATCH 03/21] undo test string --- core/src/components/alert/test/basic/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/alert/test/basic/index.html b/core/src/components/alert/test/basic/index.html index 57b6464a9ed..5d1fe8e0a3a 100644 --- a/core/src/components/alert/test/basic/index.html +++ b/core/src/components/alert/test/basic/index.html @@ -66,7 +66,7 @@ await alertController.componentOnReady(); const alert = await alertController.create({ header: 'Alert', - message: ' Date: Fri, 19 Apr 2019 16:06:43 -0400 Subject: [PATCH 04/21] update style --- core/src/utils/sanitization/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index 7a5ecc7f158..b1ce23a37de 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -6,15 +6,15 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | undefined => { if (untrustedString === undefined) { return untrustedString; } - const whitelistedAttribs = ['class', 'id', 'href', 'src']; + const whitelistedAttributes = ['class', 'id', 'href', 'src']; const range = document.createRange(); const documentFragment = range.createContextualFragment(untrustedString); - for (const childEl of (documentFragment.children as any)) { - for (const attributeName of childEl.getAttributeNames()) { + for (let childEl of (documentFragment.children as any)) { + for (let attributeName of childEl.getAttributeNames()) { // remove non-whitelisted attribs - if (!whitelistedAttribs.includes(attributeName.toLowerCase())) { + if (!whitelistedAttributes.includes(attributeName.toLowerCase())) { childEl.removeAttribute(attributeName); continue; } From 4ee2891d740999a5da1e37f4c088b964622996f0 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Mon, 22 Apr 2019 10:04:59 -0400 Subject: [PATCH 05/21] run linter --- core/src/utils/sanitization/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index b1ce23a37de..9a19b14569c 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -10,8 +10,8 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | const range = document.createRange(); const documentFragment = range.createContextualFragment(untrustedString); - for (let childEl of (documentFragment.children as any)) { - for (let attributeName of childEl.getAttributeNames()) { + for (const childEl of (documentFragment.children as any)) { + for (const attributeName of childEl.getAttributeNames()) { // remove non-whitelisted attribs if (!whitelistedAttributes.includes(attributeName.toLowerCase())) { From 6e34401bb323d3fe6278cdbf32efac74bc5c456b Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 23 Apr 2019 12:24:29 -0400 Subject: [PATCH 06/21] change !== to != --- core/src/utils/sanitization/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index 9a19b14569c..26b5a659ede 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -24,7 +24,7 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | const attributeValue = childEl.getAttribute(attributeName); /* tslint:disable-next-line */ - if (attributeValue !== null && attributeValue.toLowerCase().includes('javascript:')) { + if (attributeValue != null && attributeValue.toLowerCase().includes('javascript:')) { childEl.removeAttribute(attributeName); } } From 55880a741b78aa0ab96baa5594f3fd49a0f4b622 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 23 Apr 2019 13:08:35 -0400 Subject: [PATCH 07/21] wrap sanitize in try catch, add case for numbers --- core/src/utils/sanitization/index.ts | 42 +++++++++++++++------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index 26b5a659ede..a6c1709662c 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -4,31 +4,35 @@ */ export const sanitizeDOMString = (untrustedString: string | undefined): string | undefined => { - if (untrustedString === undefined) { return untrustedString; } + try { + if (typeof untrustedString !== 'string') { return untrustedString; } - const whitelistedAttributes = ['class', 'id', 'href', 'src']; - const range = document.createRange(); - const documentFragment = range.createContextualFragment(untrustedString); + const whitelistedAttributes = ['class', 'id', 'href', 'src']; + const range = document.createRange(); + const documentFragment = range.createContextualFragment(untrustedString); - for (const childEl of (documentFragment.children as any)) { - for (const attributeName of childEl.getAttributeNames()) { + for (const childEl of (documentFragment.children as any)) { + for (const attributeName of childEl.getAttributeNames()) { - // remove non-whitelisted attribs - if (!whitelistedAttributes.includes(attributeName.toLowerCase())) { - childEl.removeAttribute(attributeName); - continue; - } + // remove non-whitelisted attribs + if (!whitelistedAttributes.includes(attributeName.toLowerCase())) { + childEl.removeAttribute(attributeName); + continue; + } - // clean up any whitelisted attribs - // that attempt to do any JS funny-business - const attributeValue = childEl.getAttribute(attributeName); + // clean up any whitelisted attribs + // that attempt to do any JS funny-business + const attributeValue = childEl.getAttribute(attributeName); - /* tslint:disable-next-line */ - if (attributeValue != null && attributeValue.toLowerCase().includes('javascript:')) { - childEl.removeAttribute(attributeName); + /* tslint:disable-next-line */ + if (attributeValue != null && attributeValue.toLowerCase().includes('javascript:')) { + childEl.removeAttribute(attributeName); + } } } - } - return new XMLSerializer().serializeToString(documentFragment); + return new XMLSerializer().serializeToString(documentFragment); + } catch (err) { + throw err; + } }; From 7eefd5fa2146b945467ffa140b7daec363c872e1 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 23 Apr 2019 14:38:21 -0400 Subject: [PATCH 08/21] update docs to mention sanitizing --- core/src/components.d.ts | 20 ++++++------- core/src/components/alert/alert.tsx | 4 +++ core/src/components/alert/readme.md | 30 +++++++++---------- .../infinite-scroll-content.tsx | 4 +++ .../infinite-scroll-content/readme.md | 8 ++--- .../components/refresher-content/readme.md | 12 ++++---- .../refresher-content/refresher-content.tsx | 10 +++++++ core/src/components/searchbar/readme.md | 2 +- core/src/components/searchbar/searchbar.tsx | 4 +++ 9 files changed, 58 insertions(+), 36 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 242f17ba156..5090ec975ef 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -285,7 +285,7 @@ export namespace Components { */ 'leaveAnimation'?: AnimationBuilder; /** - * The main message to be displayed in the alert. + * 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 `` would become `<Ionic>` */ 'message'?: string; /** @@ -352,7 +352,7 @@ export namespace Components { */ 'leaveAnimation'?: AnimationBuilder; /** - * The main message to be displayed in the alert. + * 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 `` would become `<Ionic>` */ 'message'?: string; /** @@ -1566,7 +1566,7 @@ export namespace Components { */ 'loadingSpinner'?: SpinnerTypes | null; /** - * Optional text to display while loading. + * 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 `` would become `<Ionic>` */ 'loadingText'?: string; } @@ -1576,7 +1576,7 @@ export namespace Components { */ 'loadingSpinner'?: SpinnerTypes | null; /** - * Optional text to display while loading. + * 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 `` would become `<Ionic>` */ 'loadingText'?: string; } @@ -3418,7 +3418,7 @@ export namespace Components { */ 'pullingIcon'?: string | null; /** - * The text you want to display when you begin to pull down + * The text you want to display when you begin to pull down Optional text to display while loading. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` */ 'pullingText'?: string; /** @@ -3426,7 +3426,7 @@ export namespace Components { */ 'refreshingSpinner'?: SpinnerTypes | null; /** - * The text you want to display when performing a refresh + * The text you want to display when performing a refresh Optional text to display while loading. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` */ 'refreshingText'?: string; } @@ -3436,7 +3436,7 @@ export namespace Components { */ 'pullingIcon'?: string | null; /** - * The text you want to display when you begin to pull down + * The text you want to display when you begin to pull down Optional text to display while loading. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` */ 'pullingText'?: string; /** @@ -3444,7 +3444,7 @@ export namespace Components { */ 'refreshingSpinner'?: SpinnerTypes | null; /** - * The text you want to display when performing a refresh + * The text you want to display when performing a refresh Optional text to display while loading. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` */ 'refreshingText'?: string; } @@ -3732,7 +3732,7 @@ export namespace Components { */ 'mode': Mode; /** - * Set the input's placeholder. + * Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` */ 'placeholder': string; /** @@ -3826,7 +3826,7 @@ export namespace Components { */ 'onIonInput'?: (event: CustomEvent) => void; /** - * Set the input's placeholder. + * Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` */ 'placeholder'?: string; /** diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index d6350ed241a..7405ca8a89a 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -73,6 +73,10 @@ export class Alert implements ComponentInterface, OverlayInterface { /** * 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 `` would become + * `<Ionic>` */ @Prop() message?: string; diff --git a/core/src/components/alert/readme.md b/core/src/components/alert/readme.md index 8b94a1f129b..7f36de99376 100644 --- a/core/src/components/alert/readme.md +++ b/core/src/components/alert/readme.md @@ -1054,21 +1054,21 @@ export default { ## Properties -| Property | Attribute | Description | Type | Default | -| ----------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------- | -| `animated` | `animated` | If `true`, the alert will animate. | `boolean` | `true` | -| `backdropDismiss` | `backdrop-dismiss` | If `true`, the alert will be dismissed when the backdrop is clicked. | `boolean` | `true` | -| `buttons` | -- | Array of buttons to be added to the alert. | `(string \| AlertButton)[]` | `[]` | -| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` | -| `enterAnimation` | -- | Animation to use when the alert is presented. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | -| `header` | `header` | The main title in the heading of the alert. | `string \| undefined` | `undefined` | -| `inputs` | -- | Array of input to show in the alert. | `AlertInput[]` | `[]` | -| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` | -| `leaveAnimation` | -- | Animation to use when the alert is dismissed. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | -| `message` | `message` | The main message to be displayed in the alert. | `string \| undefined` | `undefined` | -| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` | -| `subHeader` | `sub-header` | The subtitle in the heading of the alert. Displayed under the title. | `string \| undefined` | `undefined` | -| `translucent` | `translucent` | If `true`, the alert will be translucent. | `boolean` | `false` | +| Property | Attribute | Description | Type | Default | +| ----------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ----------- | +| `animated` | `animated` | If `true`, the alert will animate. | `boolean` | `true` | +| `backdropDismiss` | `backdrop-dismiss` | If `true`, the alert will be dismissed when the backdrop is clicked. | `boolean` | `true` | +| `buttons` | -- | Array of buttons to be added to the alert. | `(string \| AlertButton)[]` | `[]` | +| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` | +| `enterAnimation` | -- | Animation to use when the alert is presented. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | +| `header` | `header` | The main title in the heading of the alert. | `string \| undefined` | `undefined` | +| `inputs` | -- | Array of input to show in the alert. | `AlertInput[]` | `[]` | +| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` | +| `leaveAnimation` | -- | Animation to use when the alert is dismissed. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | +| `message` | `message` | 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 `` would become `<Ionic>` | `string \| undefined` | `undefined` | +| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` | +| `subHeader` | `sub-header` | The subtitle in the heading of the alert. Displayed under the title. | `string \| undefined` | `undefined` | +| `translucent` | `translucent` | If `true`, the alert will be translucent. | `boolean` | `false` | ## Events diff --git a/core/src/components/infinite-scroll-content/infinite-scroll-content.tsx b/core/src/components/infinite-scroll-content/infinite-scroll-content.tsx index 905bec78a83..03f0a3383ac 100644 --- a/core/src/components/infinite-scroll-content/infinite-scroll-content.tsx +++ b/core/src/components/infinite-scroll-content/infinite-scroll-content.tsx @@ -23,6 +23,10 @@ export class InfiniteScrollContent implements ComponentInterface { /** * 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 `` would become + * `<Ionic>` */ @Prop() loadingText?: string; diff --git a/core/src/components/infinite-scroll-content/readme.md b/core/src/components/infinite-scroll-content/readme.md index 04b3649ec83..1e66d69874f 100644 --- a/core/src/components/infinite-scroll-content/readme.md +++ b/core/src/components/infinite-scroll-content/readme.md @@ -76,10 +76,10 @@ export default Example ## Properties -| Property | Attribute | Description | Type | Default | -| ---------------- | ----------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | -| `loadingSpinner` | `loading-spinner` | An animated SVG spinner that shows while loading. | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | -| `loadingText` | `loading-text` | Optional text to display while loading. | `string \| undefined` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ---------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | +| `loadingSpinner` | `loading-spinner` | An animated SVG spinner that shows while loading. | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | +| `loadingText` | `loading-text` | 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 `` would become `<Ionic>` | `string \| undefined` | `undefined` | ---------------------------------------------- diff --git a/core/src/components/refresher-content/readme.md b/core/src/components/refresher-content/readme.md index d936d71b151..109bc40d6fc 100644 --- a/core/src/components/refresher-content/readme.md +++ b/core/src/components/refresher-content/readme.md @@ -9,12 +9,12 @@ The refresher content contains the text, icon and spinner to display during a pu ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------- | -------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | -| `pullingIcon` | `pulling-icon` | A static icon to display when you begin to pull down | `null \| string \| undefined` | `undefined` | -| `pullingText` | `pulling-text` | The text you want to display when you begin to pull down | `string \| undefined` | `undefined` | -| `refreshingSpinner` | `refreshing-spinner` | An animated SVG spinner that shows when refreshing begins | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | -| `refreshingText` | `refreshing-text` | The text you want to display when performing a refresh | `string \| undefined` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | +| `pullingIcon` | `pulling-icon` | A static icon to display when you begin to pull down | `null \| string \| undefined` | `undefined` | +| `pullingText` | `pulling-text` | The text you want to display when you begin to pull down Optional text to display while loading. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` | `string \| undefined` | `undefined` | +| `refreshingSpinner` | `refreshing-spinner` | An animated SVG spinner that shows when refreshing begins | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | +| `refreshingText` | `refreshing-text` | The text you want to display when performing a refresh Optional text to display while loading. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` | `string \| undefined` | `undefined` | ---------------------------------------------- diff --git a/core/src/components/refresher-content/refresher-content.tsx b/core/src/components/refresher-content/refresher-content.tsx index 218f07eca6f..15a1a80fda3 100644 --- a/core/src/components/refresher-content/refresher-content.tsx +++ b/core/src/components/refresher-content/refresher-content.tsx @@ -19,6 +19,11 @@ export class RefresherContent implements ComponentInterface { /** * The text you want to display when you begin to pull down + * Optional text to display while loading. + * `pullingText` can accept either plaintext or HTML as a string. + * To display characters normally reserved for HTML, they + * must be escaped. For example `` would become + * `<Ionic>` */ @Prop() pullingText?: string; @@ -29,6 +34,11 @@ export class RefresherContent implements ComponentInterface { /** * The text you want to display when performing a refresh + * Optional text to display while loading. + * `refreshingText` can accept either plaintext or HTML as a string. + * To display characters normally reserved for HTML, they + * must be escaped. For example `` would become + * `<Ionic>` */ @Prop() refreshingText?: string; diff --git a/core/src/components/searchbar/readme.md b/core/src/components/searchbar/readme.md index d59a3411aa2..08dbfcce052 100644 --- a/core/src/components/searchbar/readme.md +++ b/core/src/components/searchbar/readme.md @@ -186,7 +186,7 @@ export default Example; | `debounce` | `debounce` | Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. | `number` | `250` | | `disabled` | `disabled` | If `true`, the user cannot interact with the input. | `boolean` | `false` | | `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` | -| `placeholder` | `placeholder` | Set the input's placeholder. | `string` | `'Search'` | +| `placeholder` | `placeholder` | Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` | `string` | `'Search'` | | `searchIcon` | `search-icon` | The icon to use as the search icon. | `string` | `'search'` | | `showCancelButton` | `show-cancel-button` | If `true`, show the cancel button. | `boolean` | `false` | | `spellcheck` | `spellcheck` | If `true`, enable spellcheck on the input. | `boolean` | `false` | diff --git a/core/src/components/searchbar/searchbar.tsx b/core/src/components/searchbar/searchbar.tsx index 7a42f157001..18a17b99ae2 100644 --- a/core/src/components/searchbar/searchbar.tsx +++ b/core/src/components/searchbar/searchbar.tsx @@ -86,6 +86,10 @@ export class Searchbar implements ComponentInterface { /** * Set the input's placeholder. + * `placeholder` can accept either plaintext or HTML as a string. + * To display characters normally reserved for HTML, they + * must be escaped. For example `` would become + * `<Ionic>` */ @Prop() placeholder = 'Search'; From b67667f07013eff0185dc9cb70e142b00fced3b8 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 23 Apr 2019 14:40:44 -0400 Subject: [PATCH 09/21] remove extra docs --- core/src/components.d.ts | 8 ++++---- core/src/components/refresher-content/readme.md | 12 ++++++------ .../refresher-content/refresher-content.tsx | 6 ++---- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 5090ec975ef..96ace0b991e 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -3418,7 +3418,7 @@ export namespace Components { */ 'pullingIcon'?: string | null; /** - * The text you want to display when you begin to pull down Optional text to display while loading. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` + * 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 `` would become `<Ionic>` */ 'pullingText'?: string; /** @@ -3426,7 +3426,7 @@ export namespace Components { */ 'refreshingSpinner'?: SpinnerTypes | null; /** - * The text you want to display when performing a refresh Optional text to display while loading. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` + * 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 `` would become `<Ionic>` */ 'refreshingText'?: string; } @@ -3436,7 +3436,7 @@ export namespace Components { */ 'pullingIcon'?: string | null; /** - * The text you want to display when you begin to pull down Optional text to display while loading. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` + * 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 `` would become `<Ionic>` */ 'pullingText'?: string; /** @@ -3444,7 +3444,7 @@ export namespace Components { */ 'refreshingSpinner'?: SpinnerTypes | null; /** - * The text you want to display when performing a refresh Optional text to display while loading. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` + * 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 `` would become `<Ionic>` */ 'refreshingText'?: string; } diff --git a/core/src/components/refresher-content/readme.md b/core/src/components/refresher-content/readme.md index 109bc40d6fc..cd20f3fda4d 100644 --- a/core/src/components/refresher-content/readme.md +++ b/core/src/components/refresher-content/readme.md @@ -9,12 +9,12 @@ The refresher content contains the text, icon and spinner to display during a pu ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | -| `pullingIcon` | `pulling-icon` | A static icon to display when you begin to pull down | `null \| string \| undefined` | `undefined` | -| `pullingText` | `pulling-text` | The text you want to display when you begin to pull down Optional text to display while loading. `pullingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` | `string \| undefined` | `undefined` | -| `refreshingSpinner` | `refreshing-spinner` | An animated SVG spinner that shows when refreshing begins | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | -| `refreshingText` | `refreshing-text` | The text you want to display when performing a refresh Optional text to display while loading. `refreshingText` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` | `string \| undefined` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | +| `pullingIcon` | `pulling-icon` | A static icon to display when you begin to pull down | `null \| string \| undefined` | `undefined` | +| `pullingText` | `pulling-text` | 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 `` would become `<Ionic>` | `string \| undefined` | `undefined` | +| `refreshingSpinner` | `refreshing-spinner` | An animated SVG spinner that shows when refreshing begins | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | +| `refreshingText` | `refreshing-text` | 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 `` would become `<Ionic>` | `string \| undefined` | `undefined` | ---------------------------------------------- diff --git a/core/src/components/refresher-content/refresher-content.tsx b/core/src/components/refresher-content/refresher-content.tsx index 15a1a80fda3..410be79403a 100644 --- a/core/src/components/refresher-content/refresher-content.tsx +++ b/core/src/components/refresher-content/refresher-content.tsx @@ -18,8 +18,7 @@ export class RefresherContent implements ComponentInterface { @Prop({ mutable: true }) pullingIcon?: string | null; /** - * The text you want to display when you begin to pull down - * Optional text to display while loading. + * 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 `` would become @@ -33,8 +32,7 @@ export class RefresherContent implements ComponentInterface { @Prop({ mutable: true }) refreshingSpinner?: SpinnerTypes | null; /** - * The text you want to display when performing a refresh - * Optional text to display while loading. + * 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 `` would become From c6158b8756f0d1b1033d770ff9a7e20aa6a218dc Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 23 Apr 2019 14:45:47 -0400 Subject: [PATCH 10/21] change throw to console.error --- core/src/utils/sanitization/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index a6c1709662c..add6cf1e92d 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -33,6 +33,6 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | return new XMLSerializer().serializeToString(documentFragment); } catch (err) { - throw err; + console.error(err); } }; From 09dd0caebafdb6648327351c8bf4a20e8545e497 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 23 Apr 2019 14:48:21 -0400 Subject: [PATCH 11/21] return empty string after erroring --- core/src/utils/sanitization/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index add6cf1e92d..436fa5e8338 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -34,5 +34,6 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | return new XMLSerializer().serializeToString(documentFragment); } catch (err) { console.error(err); + return ''; } }; From 8c116c930e1ee83207aa4a7c5dd8ec1c8e6913a6 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 23 Apr 2019 16:56:39 -0400 Subject: [PATCH 12/21] add blacklisted tags, add support to older chrome browsers --- core/src/utils/sanitization/index.ts | 54 +++++++++++++++++++-- core/src/utils/sanitization/test/e2e.ts | 1 + core/src/utils/sanitization/test/index.html | 5 ++ 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index 436fa5e8338..526a1ce3401 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -4,36 +4,82 @@ */ export const sanitizeDOMString = (untrustedString: string | undefined): string | undefined => { + + const div = document.createElement('div'); + document.body.appendChild(div); + try { if (typeof untrustedString !== 'string') { return untrustedString; } const whitelistedAttributes = ['class', 'id', 'href', 'src']; + const blacklistedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed']; const range = document.createRange(); + + /** + * Older version of Chrome require that we + * explicitly create and select a context node + */ + range.selectNode(div); + const documentFragment = range.createContextualFragment(untrustedString); + /** + * Remove any elements + * that are blacklisted + */ + for (const blacklistedTag of blacklistedTags) { + const getElementsToRemove = documentFragment.querySelectorAll(blacklistedTag); + getElementsToRemove.forEach(element => { + documentFragment.removeChild(element); + }); + } + for (const childEl of (documentFragment.children as any)) { - for (const attributeName of childEl.getAttributeNames()) { + + /** + * Since childEl.attributes is a live object + * we need to process all attributes before + * we can remove any of them + */ + const attributesToRemove = []; + for (const attribute of childEl.attributes) { + const attributeName = attribute.name; // remove non-whitelisted attribs if (!whitelistedAttributes.includes(attributeName.toLowerCase())) { - childEl.removeAttribute(attributeName); + attributesToRemove.push(attributeName); continue; } // clean up any whitelisted attribs // that attempt to do any JS funny-business - const attributeValue = childEl.getAttribute(attributeName); + const attributeValue = attribute.value; /* tslint:disable-next-line */ if (attributeValue != null && attributeValue.toLowerCase().includes('javascript:')) { - childEl.removeAttribute(attributeName); + attributesToRemove.push(attributeName); } } + + /** + * Finally, remove all marked attributes from element + */ + for (const attributeName of attributesToRemove) { + childEl.removeAttribute(attributeName); + } } + // Remove context node from DOM + document.body.removeChild(div); + return new XMLSerializer().serializeToString(documentFragment); + } catch (err) { console.error(err); + + // Remove context node from DOM + document.body.removeChild(div); + return ''; } }; diff --git a/core/src/utils/sanitization/test/e2e.ts b/core/src/utils/sanitization/test/e2e.ts index 106292ac8ba..9a19cceb10a 100644 --- a/core/src/utils/sanitization/test/e2e.ts +++ b/core/src/utils/sanitization/test/e2e.ts @@ -16,6 +16,7 @@ test('sanitization:', async done => { await page.click('#testB'); await page.click('#testC'); await page.click('#testD'); + await page.click('#testE'); done(); diff --git a/core/src/utils/sanitization/test/index.html b/core/src/utils/sanitization/test/index.html index 038ba300b86..1da5479bc6b 100644 --- a/core/src/utils/sanitization/test/index.html +++ b/core/src/utils/sanitization/test/index.html @@ -38,6 +38,7 @@ Test B Test C Test D + Test E @@ -81,6 +82,10 @@ const link = results.querySelector('a'); link.click(); } + + function testE() { + runTest(''); + } From ed749129a1e7eaf8fa07924430ba2706ff14342a Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 23 Apr 2019 17:00:45 -0400 Subject: [PATCH 13/21] fix typo --- core/src/utils/sanitization/test/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/utils/sanitization/test/index.html b/core/src/utils/sanitization/test/index.html index 1da5479bc6b..559575ba31c 100644 --- a/core/src/utils/sanitization/test/index.html +++ b/core/src/utils/sanitization/test/index.html @@ -84,7 +84,7 @@ } function testE() { - runTest(''); + runTest(''); } From b92f19535d63b4ac143c5304494971f1bdc146d0 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Wed, 24 Apr 2019 10:23:41 -0400 Subject: [PATCH 14/21] add recursive step to get nested children, update per comments --- core/src/utils/sanitization/index.ts | 94 +++++++++++++-------- core/src/utils/sanitization/test/e2e.ts | 3 + core/src/utils/sanitization/test/index.html | 21 +++++ 3 files changed, 84 insertions(+), 34 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index 526a1ce3401..79c3275255e 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -11,7 +11,6 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | try { if (typeof untrustedString !== 'string') { return untrustedString; } - const whitelistedAttributes = ['class', 'id', 'href', 'src']; const blacklistedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed']; const range = document.createRange(); @@ -27,46 +26,37 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | * Remove any elements * that are blacklisted */ - for (const blacklistedTag of blacklistedTags) { - const getElementsToRemove = documentFragment.querySelectorAll(blacklistedTag); - getElementsToRemove.forEach(element => { - documentFragment.removeChild(element); - }); - } - for (const childEl of (documentFragment.children as any)) { + /* tslint:disable-next-line */ + for (let i = 0; i < blacklistedTags.length; i++) { + const getElementsToRemove = documentFragment.querySelectorAll(blacklistedTags[i]); - /** - * Since childEl.attributes is a live object - * we need to process all attributes before - * we can remove any of them - */ - const attributesToRemove = []; - for (const attribute of childEl.attributes) { - const attributeName = attribute.name; - - // remove non-whitelisted attribs - if (!whitelistedAttributes.includes(attributeName.toLowerCase())) { - attributesToRemove.push(attributeName); - continue; + for (let elementIndex = getElementsToRemove.length - 1; elementIndex >= 0; elementIndex--) { + const element = getElementsToRemove[elementIndex]; + if (element.parentNode) { + element.parentNode.removeChild(element); + } else { + documentFragment.removeChild(element); } - // clean up any whitelisted attribs - // that attempt to do any JS funny-business - const attributeValue = attribute.value; - - /* tslint:disable-next-line */ - if (attributeValue != null && attributeValue.toLowerCase().includes('javascript:')) { - attributesToRemove.push(attributeName); + /** + * We still need to sanitize + * the children of this element + * as they are left behind + */ + for (let childIndex = element.children.length - 1; childIndex >= 0; childIndex--) { + sanitizeElement(element.children[childIndex]); } } + } - /** - * Finally, remove all marked attributes from element - */ - for (const attributeName of attributesToRemove) { - childEl.removeAttribute(attributeName); - } + /** + * Go through remaining + * elements and remove + * non-whitelisted attribs + */ + for (const childEl of (documentFragment.children as any)) { + sanitizeElement(childEl); } // Remove context node from DOM @@ -83,3 +73,39 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | return ''; } }; + +/** + * Clean up current element based on whitelisted attributes + * and then recursively dig down into any child elements to + * clean those up as well + */ +const sanitizeElement = (element: any) => { + const whitelistedAttributes = ['class', 'id', 'href', 'src']; + + for (let i = element.attributes.length - 1; i >= 0; i--) { + const attribute = element.attributes[i]; + const attributeName = attribute.name; + + // remove non-whitelisted attribs + if (!whitelistedAttributes.includes(attributeName.toLowerCase())) { + element.removeAttribute(attributeName); + continue; + } + + // clean up any whitelisted attribs + // that attempt to do any JS funny-business + const attributeValue = attribute.value; + + /* tslint:disable-next-line */ + if (attributeValue != null && attributeValue.toLowerCase().includes('javascript:')) { + element.removeAttribute(attributeName); + } + } + + /** + * Sanitize any nested children + */ + for (let i = element.children.length - 1; i >= 0; i--) { + sanitizeElement(element.children[i]); + } +}; diff --git a/core/src/utils/sanitization/test/e2e.ts b/core/src/utils/sanitization/test/e2e.ts index 9a19cceb10a..513946f5734 100644 --- a/core/src/utils/sanitization/test/e2e.ts +++ b/core/src/utils/sanitization/test/e2e.ts @@ -17,6 +17,9 @@ test('sanitization:', async done => { await page.click('#testC'); await page.click('#testD'); await page.click('#testE'); + await page.click('#testF'); + await page.click('#testG'); + await page.click('#testH'); done(); diff --git a/core/src/utils/sanitization/test/index.html b/core/src/utils/sanitization/test/index.html index 559575ba31c..fed2f3b7a53 100644 --- a/core/src/utils/sanitization/test/index.html +++ b/core/src/utils/sanitization/test/index.html @@ -39,6 +39,9 @@ Test C Test D Test E + Test F + Test G + Test H @@ -86,6 +89,24 @@ function testE() { runTest(''); } + + function testF() { + runTest(''); + + const link = results.querySelector('a'); + link.click(); + } + + function testG() { + runTest(''); + } + + function testH() { + runTest('Hello!Click me'); + + const button = results.querySelector('ion-button'); + button.click(); + } From 24551b14f35104c9e7b29d60df6e5642ae06ed04 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Wed, 24 Apr 2019 10:37:03 -0400 Subject: [PATCH 15/21] update wording to use blocked and allowed --- core/src/utils/sanitization/index.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index 79c3275255e..017da87e1e2 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -11,7 +11,7 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | try { if (typeof untrustedString !== 'string') { return untrustedString; } - const blacklistedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed']; + const blockedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed']; const range = document.createRange(); /** @@ -24,12 +24,12 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | /** * Remove any elements - * that are blacklisted + * that are blocked */ /* tslint:disable-next-line */ - for (let i = 0; i < blacklistedTags.length; i++) { - const getElementsToRemove = documentFragment.querySelectorAll(blacklistedTags[i]); + for (let i = 0; i < blockedTags.length; i++) { + const getElementsToRemove = documentFragment.querySelectorAll(blockedTags[i]); for (let elementIndex = getElementsToRemove.length - 1; elementIndex >= 0; elementIndex--) { const element = getElementsToRemove[elementIndex]; @@ -53,7 +53,7 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | /** * Go through remaining * elements and remove - * non-whitelisted attribs + * non-allowed attribs */ for (const childEl of (documentFragment.children as any)) { sanitizeElement(childEl); @@ -75,24 +75,24 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | }; /** - * Clean up current element based on whitelisted attributes + * Clean up current element based on allowed attributes * and then recursively dig down into any child elements to * clean those up as well */ const sanitizeElement = (element: any) => { - const whitelistedAttributes = ['class', 'id', 'href', 'src']; + const allowedAttributes = ['class', 'id', 'href', 'src']; for (let i = element.attributes.length - 1; i >= 0; i--) { const attribute = element.attributes[i]; const attributeName = attribute.name; - // remove non-whitelisted attribs - if (!whitelistedAttributes.includes(attributeName.toLowerCase())) { + // remove non-allowed attribs + if (!allowedAttributes.includes(attributeName.toLowerCase())) { element.removeAttribute(attributeName); continue; } - // clean up any whitelisted attribs + // clean up any allowed attribs // that attempt to do any JS funny-business const attributeValue = attribute.value; From 4aa5a53ae5810352ea9065c1726ca003cacdf0ff Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Wed, 24 Apr 2019 12:17:09 -0400 Subject: [PATCH 16/21] add compatibility for older browsers --- core/src/utils/sanitization/index.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index 017da87e1e2..b9802040a76 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -30,7 +30,6 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | /* tslint:disable-next-line */ for (let i = 0; i < blockedTags.length; i++) { const getElementsToRemove = documentFragment.querySelectorAll(blockedTags[i]); - for (let elementIndex = getElementsToRemove.length - 1; elementIndex >= 0; elementIndex--) { const element = getElementsToRemove[elementIndex]; if (element.parentNode) { @@ -55,14 +54,21 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | * elements and remove * non-allowed attribs */ - for (const childEl of (documentFragment.children as any)) { + + // IE does not support .children on document fragments, only .childNodes + /* tslint:disable-next-line */ + const documentFragmentChildren = (documentFragment.children != null) ? documentFragment.children : documentFragment.childNodes; + for (const childEl of (documentFragmentChildren as any)) { sanitizeElement(childEl); } // Remove context node from DOM document.body.removeChild(div); - return new XMLSerializer().serializeToString(documentFragment); + // Append document fragment to div + div.appendChild(documentFragment); + + return div.innerHTML; } catch (err) { console.error(err); @@ -80,6 +86,9 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | * clean those up as well */ const sanitizeElement = (element: any) => { + // IE uses childNodes, so ignore nodes that are not elements + if (element.nodeType && element.nodeType !== 1) { return; } + const allowedAttributes = ['class', 'id', 'href', 'src']; for (let i = element.attributes.length - 1; i >= 0; i--) { From 01fdccf8677c573cf9bec0e9c594b4a5986d4722 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Wed, 24 Apr 2019 12:54:35 -0400 Subject: [PATCH 17/21] clean up --- core/src/utils/sanitization/index.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index b9802040a76..3ab8c909063 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -16,7 +16,7 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | /** * Older version of Chrome require that we - * explicitly create and select a context node + * explicitly select a context node */ range.selectNode(div); @@ -26,10 +26,9 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | * Remove any elements * that are blocked */ + blockedTags.forEach(blockedTag => { - /* tslint:disable-next-line */ - for (let i = 0; i < blockedTags.length; i++) { - const getElementsToRemove = documentFragment.querySelectorAll(blockedTags[i]); + const getElementsToRemove = documentFragment.querySelectorAll(blockedTag); for (let elementIndex = getElementsToRemove.length - 1; elementIndex >= 0; elementIndex--) { const element = getElementsToRemove[elementIndex]; if (element.parentNode) { @@ -43,23 +42,25 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | * the children of this element * as they are left behind */ - for (let childIndex = element.children.length - 1; childIndex >= 0; childIndex--) { + /* tslint:disable-next-line */ + for (let childIndex = 0; childIndex < element.children.length; childIndex++) { sanitizeElement(element.children[childIndex]); } } - } + }); /** - * Go through remaining - * elements and remove + * Go through remaining elements and remove * non-allowed attribs */ // IE does not support .children on document fragments, only .childNodes /* tslint:disable-next-line */ const documentFragmentChildren = (documentFragment.children != null) ? documentFragment.children : documentFragment.childNodes; - for (const childEl of (documentFragmentChildren as any)) { - sanitizeElement(childEl); + + /* tslint:disable-next-line */ + for (let childIndex = 0; childIndex < documentFragmentChildren.length; childIndex++) { + sanitizeElement(documentFragmentChildren[childIndex]); } // Remove context node from DOM @@ -114,7 +115,8 @@ const sanitizeElement = (element: any) => { /** * Sanitize any nested children */ - for (let i = element.children.length - 1; i >= 0; i--) { + /* tslint:disable-next-line */ + for (let i = 0; i < element.children.length; i++) { sanitizeElement(element.children[i]); } }; From f17e895135af86e0e21998b490fb2c1031264904 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Wed, 24 Apr 2019 14:00:06 -0400 Subject: [PATCH 18/21] use document fragment, clean up code --- core/src/utils/sanitization/index.ts | 53 ++++++++++++++++------------ 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index 3ab8c909063..7bb75b7b0d4 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -4,23 +4,20 @@ */ export const sanitizeDOMString = (untrustedString: string | undefined): string | undefined => { - - const div = document.createElement('div'); - document.body.appendChild(div); - try { if (typeof untrustedString !== 'string') { return untrustedString; } const blockedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed']; - const range = document.createRange(); /** - * Older version of Chrome require that we - * explicitly select a context node + * Create a document fragment + * separate from the main DOM, + * create a div to do our work in */ - range.selectNode(div); - - const documentFragment = range.createContextualFragment(untrustedString); + const documentFragment = document.createDocumentFragment(); + const workingDiv = document.createElement('div'); + documentFragment.appendChild(workingDiv); + workingDiv.innerHTML = untrustedString; /** * Remove any elements @@ -43,8 +40,11 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | * as they are left behind */ /* tslint:disable-next-line */ - for (let childIndex = 0; childIndex < element.children.length; childIndex++) { - sanitizeElement(element.children[childIndex]); + const childElements = getElementChildren(element); + + /* tslint:disable-next-line */ + for (let childIndex = 0; childIndex < childElements.length; childIndex++) { + sanitizeElement(childElements[childIndex]); } } }); @@ -56,27 +56,24 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | // IE does not support .children on document fragments, only .childNodes /* tslint:disable-next-line */ - const documentFragmentChildren = (documentFragment.children != null) ? documentFragment.children : documentFragment.childNodes; + const documentFragmentChildren = getElementChildren(documentFragment); /* tslint:disable-next-line */ for (let childIndex = 0; childIndex < documentFragmentChildren.length; childIndex++) { sanitizeElement(documentFragmentChildren[childIndex]); } - // Remove context node from DOM - document.body.removeChild(div); - // Append document fragment to div - div.appendChild(documentFragment); + const fragmentDiv = document.createElement('div'); + fragmentDiv.appendChild(documentFragment); - return div.innerHTML; + // First child is always the div we did our work in + const getInnerDiv = fragmentDiv.querySelector('div'); + return (getInnerDiv !== null) ? getInnerDiv.innerHTML : fragmentDiv.innerHTML; } catch (err) { console.error(err); - // Remove context node from DOM - document.body.removeChild(div); - return ''; } }; @@ -115,8 +112,18 @@ const sanitizeElement = (element: any) => { /** * Sanitize any nested children */ + const childElements = getElementChildren(element); + /* tslint:disable-next-line */ - for (let i = 0; i < element.children.length; i++) { - sanitizeElement(element.children[i]); + for (let i = 0; i < childElements.length; i++) { + sanitizeElement(childElements[i]); } }; + +/** + * IE doesn't always support .children + * so we revert to .childNodes instead + */ +const getElementChildren = (element: any) => { + return (element.children != null) ? element.children : element.childNodes; +}; From 9914386979e3c204d3567c90d948e8d268ba7473 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Fri, 26 Apr 2019 10:06:19 -0400 Subject: [PATCH 19/21] remove extra tslint lines --- core/src/utils/sanitization/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index 7bb75b7b0d4..f80de4d0ad9 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -39,7 +39,6 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | * the children of this element * as they are left behind */ - /* tslint:disable-next-line */ const childElements = getElementChildren(element); /* tslint:disable-next-line */ @@ -55,7 +54,6 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | */ // IE does not support .children on document fragments, only .childNodes - /* tslint:disable-next-line */ const documentFragmentChildren = getElementChildren(documentFragment); /* tslint:disable-next-line */ From dd9180eba9cb5eb8fd86504295b16a7c6d97825c Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Fri, 26 Apr 2019 10:36:07 -0400 Subject: [PATCH 20/21] a few more tweaks --- core/src/utils/sanitization/index.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index f80de4d0ad9..3019250f0da 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -5,9 +5,7 @@ export const sanitizeDOMString = (untrustedString: string | undefined): string | undefined => { try { - if (typeof untrustedString !== 'string') { return untrustedString; } - - const blockedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed']; + if (typeof untrustedString !== 'string' || untrustedString === '') { return untrustedString; } /** * Create a document fragment @@ -85,8 +83,6 @@ const sanitizeElement = (element: any) => { // IE uses childNodes, so ignore nodes that are not elements if (element.nodeType && element.nodeType !== 1) { return; } - const allowedAttributes = ['class', 'id', 'href', 'src']; - for (let i = element.attributes.length - 1; i >= 0; i--) { const attribute = element.attributes[i]; const attributeName = attribute.name; @@ -125,3 +121,6 @@ const sanitizeElement = (element: any) => { const getElementChildren = (element: any) => { return (element.children != null) ? element.children : element.childNodes; }; + +const allowedAttributes = ['class', 'id', 'href', 'src']; +const blockedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed']; From 4d006eeed7ab797dd0906b5e07a569e367744c82 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Fri, 26 Apr 2019 10:43:15 -0400 Subject: [PATCH 21/21] add link to security docs --- core/src/components.d.ts | 20 +++++------ core/src/components/alert/alert.tsx | 2 ++ core/src/components/alert/readme.md | 30 ++++++++-------- .../infinite-scroll-content.tsx | 2 ++ .../infinite-scroll-content/readme.md | 8 ++--- .../components/refresher-content/readme.md | 12 +++---- .../refresher-content/refresher-content.tsx | 4 +++ core/src/components/searchbar/readme.md | 36 +++++++++---------- core/src/components/searchbar/searchbar.tsx | 2 ++ 9 files changed, 63 insertions(+), 53 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 96ace0b991e..2416aff5b64 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -285,7 +285,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 `` would become `<Ionic>` + * 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'message'?: string; /** @@ -352,7 +352,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 `` would become `<Ionic>` + * 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'message'?: string; /** @@ -1566,7 +1566,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 `` would become `<Ionic>` + * 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'loadingText'?: string; } @@ -1576,7 +1576,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 `` would become `<Ionic>` + * 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'loadingText'?: string; } @@ -3418,7 +3418,7 @@ export namespace Components { */ 'pullingIcon'?: 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 `` would become `<Ionic>` + * 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'pullingText'?: string; /** @@ -3426,7 +3426,7 @@ export namespace Components { */ '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 `` would become `<Ionic>` + * 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'refreshingText'?: string; } @@ -3436,7 +3436,7 @@ export namespace Components { */ 'pullingIcon'?: 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 `` would become `<Ionic>` + * 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'pullingText'?: string; /** @@ -3444,7 +3444,7 @@ export namespace Components { */ '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 `` would become `<Ionic>` + * 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'refreshingText'?: string; } @@ -3732,7 +3732,7 @@ export namespace Components { */ 'mode': Mode; /** - * Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` + * Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'placeholder': string; /** @@ -3826,7 +3826,7 @@ export namespace Components { */ 'onIonInput'?: (event: CustomEvent) => void; /** - * Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` + * Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'placeholder'?: string; /** diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index 7405ca8a89a..5bee010cdb7 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -77,6 +77,8 @@ export class Alert implements ComponentInterface, OverlayInterface { * To display characters normally reserved for HTML, they * must be escaped. For example `` would become * `<Ionic>` + * + * For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ @Prop() message?: string; diff --git a/core/src/components/alert/readme.md b/core/src/components/alert/readme.md index 7f36de99376..e70d38ce019 100644 --- a/core/src/components/alert/readme.md +++ b/core/src/components/alert/readme.md @@ -1054,21 +1054,21 @@ export default { ## Properties -| Property | Attribute | Description | Type | Default | -| ----------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ----------- | -| `animated` | `animated` | If `true`, the alert will animate. | `boolean` | `true` | -| `backdropDismiss` | `backdrop-dismiss` | If `true`, the alert will be dismissed when the backdrop is clicked. | `boolean` | `true` | -| `buttons` | -- | Array of buttons to be added to the alert. | `(string \| AlertButton)[]` | `[]` | -| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` | -| `enterAnimation` | -- | Animation to use when the alert is presented. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | -| `header` | `header` | The main title in the heading of the alert. | `string \| undefined` | `undefined` | -| `inputs` | -- | Array of input to show in the alert. | `AlertInput[]` | `[]` | -| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` | -| `leaveAnimation` | -- | Animation to use when the alert is dismissed. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | -| `message` | `message` | 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 `` would become `<Ionic>` | `string \| undefined` | `undefined` | -| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` | -| `subHeader` | `sub-header` | The subtitle in the heading of the alert. Displayed under the title. | `string \| undefined` | `undefined` | -| `translucent` | `translucent` | If `true`, the alert will be translucent. | `boolean` | `false` | +| Property | Attribute | Description | Type | Default | +| ----------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------- | +| `animated` | `animated` | If `true`, the alert will animate. | `boolean` | `true` | +| `backdropDismiss` | `backdrop-dismiss` | If `true`, the alert will be dismissed when the backdrop is clicked. | `boolean` | `true` | +| `buttons` | -- | Array of buttons to be added to the alert. | `(string \| AlertButton)[]` | `[]` | +| `cssClass` | `css-class` | Additional classes to apply for custom CSS. If multiple classes are provided they should be separated by spaces. | `string \| string[] \| undefined` | `undefined` | +| `enterAnimation` | -- | Animation to use when the alert is presented. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | +| `header` | `header` | The main title in the heading of the alert. | `string \| undefined` | `undefined` | +| `inputs` | -- | Array of input to show in the alert. | `AlertInput[]` | `[]` | +| `keyboardClose` | `keyboard-close` | If `true`, the keyboard will be automatically dismissed when the overlay is presented. | `boolean` | `true` | +| `leaveAnimation` | -- | Animation to use when the alert is dismissed. | `((Animation: Animation, baseEl: any, opts?: any) => Promise) \| undefined` | `undefined` | +| `message` | `message` | 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` | +| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` | +| `subHeader` | `sub-header` | The subtitle in the heading of the alert. Displayed under the title. | `string \| undefined` | `undefined` | +| `translucent` | `translucent` | If `true`, the alert will be translucent. | `boolean` | `false` | ## Events diff --git a/core/src/components/infinite-scroll-content/infinite-scroll-content.tsx b/core/src/components/infinite-scroll-content/infinite-scroll-content.tsx index 03f0a3383ac..b440eb0eb4b 100644 --- a/core/src/components/infinite-scroll-content/infinite-scroll-content.tsx +++ b/core/src/components/infinite-scroll-content/infinite-scroll-content.tsx @@ -27,6 +27,8 @@ export class InfiniteScrollContent implements ComponentInterface { * To display characters normally reserved for HTML, they * must be escaped. For example `` would become * `<Ionic>` + * + * For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ @Prop() loadingText?: string; diff --git a/core/src/components/infinite-scroll-content/readme.md b/core/src/components/infinite-scroll-content/readme.md index 1e66d69874f..f0f8d980a1d 100644 --- a/core/src/components/infinite-scroll-content/readme.md +++ b/core/src/components/infinite-scroll-content/readme.md @@ -76,10 +76,10 @@ export default Example ## Properties -| Property | Attribute | Description | Type | Default | -| ---------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | -| `loadingSpinner` | `loading-spinner` | An animated SVG spinner that shows while loading. | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | -| `loadingText` | `loading-text` | 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 `` would become `<Ionic>` | `string \| undefined` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | +| `loadingSpinner` | `loading-spinner` | An animated SVG spinner that shows while loading. | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | +| `loadingText` | `loading-text` | 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` | ---------------------------------------------- diff --git a/core/src/components/refresher-content/readme.md b/core/src/components/refresher-content/readme.md index cd20f3fda4d..2d8c0fef801 100644 --- a/core/src/components/refresher-content/readme.md +++ b/core/src/components/refresher-content/readme.md @@ -9,12 +9,12 @@ The refresher content contains the text, icon and spinner to display during a pu ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | -| `pullingIcon` | `pulling-icon` | A static icon to display when you begin to pull down | `null \| string \| undefined` | `undefined` | -| `pullingText` | `pulling-text` | 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 `` would become `<Ionic>` | `string \| undefined` | `undefined` | -| `refreshingSpinner` | `refreshing-spinner` | An animated SVG spinner that shows when refreshing begins | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | -| `refreshingText` | `refreshing-text` | 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 `` would become `<Ionic>` | `string \| undefined` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | +| `pullingIcon` | `pulling-icon` | A static icon to display when you begin to pull down | `null \| string \| undefined` | `undefined` | +| `pullingText` | `pulling-text` | 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` | +| `refreshingSpinner` | `refreshing-spinner` | An animated SVG spinner that shows when refreshing begins | `"bubbles" \| "circles" \| "crescent" \| "dots" \| "lines" \| "lines-small" \| null \| undefined` | `undefined` | +| `refreshingText` | `refreshing-text` | 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 `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string \| undefined` | `undefined` | ---------------------------------------------- diff --git a/core/src/components/refresher-content/refresher-content.tsx b/core/src/components/refresher-content/refresher-content.tsx index 410be79403a..8e08642899c 100644 --- a/core/src/components/refresher-content/refresher-content.tsx +++ b/core/src/components/refresher-content/refresher-content.tsx @@ -23,6 +23,8 @@ export class RefresherContent implements ComponentInterface { * To display characters normally reserved for HTML, they * must be escaped. For example `` would become * `<Ionic>` + * + * For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ @Prop() pullingText?: string; @@ -37,6 +39,8 @@ export class RefresherContent implements ComponentInterface { * To display characters normally reserved for HTML, they * must be escaped. For example `` would become * `<Ionic>` + * + * For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ @Prop() refreshingText?: string; diff --git a/core/src/components/searchbar/readme.md b/core/src/components/searchbar/readme.md index 08dbfcce052..a08c430ca6e 100644 --- a/core/src/components/searchbar/readme.md +++ b/core/src/components/searchbar/readme.md @@ -174,24 +174,24 @@ export default Example; ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------- | -| `animated` | `animated` | If `true`, enable searchbar animation. | `boolean` | `false` | -| `autocomplete` | `autocomplete` | Set the input's autocomplete property. | `"off" \| "on"` | `'off'` | -| `autocorrect` | `autocorrect` | Set the input's autocorrect property. | `"off" \| "on"` | `'off'` | -| `cancelButtonIcon` | `cancel-button-icon` | Set the cancel button icon. Only applies to `md` mode. | `string` | `'md-arrow-back'` | -| `cancelButtonText` | `cancel-button-text` | Set the the cancel button text. Only applies to `ios` mode. | `string` | `'Cancel'` | -| `clearIcon` | `clear-icon` | Set the clear icon. Defaults to `"close-circle"` for `ios` and `"close"` for `md`. | `string \| undefined` | `undefined` | -| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` | -| `debounce` | `debounce` | Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. | `number` | `250` | -| `disabled` | `disabled` | If `true`, the user cannot interact with the input. | `boolean` | `false` | -| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` | -| `placeholder` | `placeholder` | Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` | `string` | `'Search'` | -| `searchIcon` | `search-icon` | The icon to use as the search icon. | `string` | `'search'` | -| `showCancelButton` | `show-cancel-button` | If `true`, show the cancel button. | `boolean` | `false` | -| `spellcheck` | `spellcheck` | If `true`, enable spellcheck on the input. | `boolean` | `false` | -| `type` | `type` | Set the type of the input. | `"email" \| "number" \| "password" \| "search" \| "tel" \| "text" \| "url"` | `'search'` | -| `value` | `value` | the value of the searchbar. | `null \| string \| undefined` | `''` | +| Property | Attribute | Description | Type | Default | +| ------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------- | +| `animated` | `animated` | If `true`, enable searchbar animation. | `boolean` | `false` | +| `autocomplete` | `autocomplete` | Set the input's autocomplete property. | `"off" \| "on"` | `'off'` | +| `autocorrect` | `autocorrect` | Set the input's autocorrect property. | `"off" \| "on"` | `'off'` | +| `cancelButtonIcon` | `cancel-button-icon` | Set the cancel button icon. Only applies to `md` mode. | `string` | `'md-arrow-back'` | +| `cancelButtonText` | `cancel-button-text` | Set the the cancel button text. Only applies to `ios` mode. | `string` | `'Cancel'` | +| `clearIcon` | `clear-icon` | Set the clear icon. Defaults to `"close-circle"` for `ios` and `"close"` for `md`. | `string \| undefined` | `undefined` | +| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` | +| `debounce` | `debounce` | Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. | `number` | `250` | +| `disabled` | `disabled` | If `true`, the user cannot interact with the input. | `boolean` | `false` | +| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` | +| `placeholder` | `placeholder` | Set the input's placeholder. `placeholder` can accept either plaintext or HTML as a string. To display characters normally reserved for HTML, they must be escaped. For example `` would become `<Ionic>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) | `string` | `'Search'` | +| `searchIcon` | `search-icon` | The icon to use as the search icon. | `string` | `'search'` | +| `showCancelButton` | `show-cancel-button` | If `true`, show the cancel button. | `boolean` | `false` | +| `spellcheck` | `spellcheck` | If `true`, enable spellcheck on the input. | `boolean` | `false` | +| `type` | `type` | Set the type of the input. | `"email" \| "number" \| "password" \| "search" \| "tel" \| "text" \| "url"` | `'search'` | +| `value` | `value` | the value of the searchbar. | `null \| string \| undefined` | `''` | ## Events diff --git a/core/src/components/searchbar/searchbar.tsx b/core/src/components/searchbar/searchbar.tsx index 18a17b99ae2..22c11c9d82a 100644 --- a/core/src/components/searchbar/searchbar.tsx +++ b/core/src/components/searchbar/searchbar.tsx @@ -90,6 +90,8 @@ export class Searchbar implements ComponentInterface { * To display characters normally reserved for HTML, they * must be escaped. For example `` would become * `<Ionic>` + * + * For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ @Prop() placeholder = 'Search';