diff --git a/CHANGELOG.md b/CHANGELOG.md index ba8d68f404..ae91b6aec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [4.3.1](https://github.com/ionic-team/ionic/compare/v4.3.0...v4.3.1) (2019-04-26) + + +### Bug Fixes + +* **angular:** support replaceUrl with angular <7.2 ([#18106](https://github.com/ionic-team/ionic/issues/18106)) ([eb3cbe4](https://github.com/ionic-team/ionic/commit/eb3cbe4)) +* sanitize components using innerHTML ([#18146](https://github.com/ionic-team/ionic/issues/18146)) ([b839e6f](https://github.com/ionic-team/ionic/commit/b839e6f)) + + + # [4.3.0 Lithium](https://github.com/ionic-team/ionic/compare/v4.2.0...v4.3.0) (2019-04-17) diff --git a/angular/package.json b/angular/package.json index 0b9598d344..dbb841ced7 100644 --- a/angular/package.json +++ b/angular/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/angular", - "version": "4.3.0", + "version": "4.3.1", "description": "Angular specific wrappers for @ionic/core", "keywords": [ "ionic", @@ -45,7 +45,7 @@ "css/" ], "dependencies": { - "@ionic/core": "4.3.0", + "@ionic/core": "4.3.1", "tslib": "^1.9.3" }, "peerDependencies": { diff --git a/angular/src/directives/navigation/stack-controller.ts b/angular/src/directives/navigation/stack-controller.ts index 0680278c31..a06eb0491c 100644 --- a/angular/src/directives/navigation/stack-controller.ts +++ b/angular/src/directives/navigation/stack-controller.ts @@ -59,7 +59,22 @@ export class StackController { } const viewsSnapshot = this.views.slice(); - const currentNavigation = this.router.getCurrentNavigation(); + let currentNavigation; + + const router = (this.router as any); + + // Angular >= 7.2.0 + if (router.getCurrentNavigation) { + currentNavigation = router.getCurrentNavigation(); + + // Angular < 7.2.0 + } else if ( + router.navigations && + router.navigations.value + ) { + currentNavigation = router.navigations.value; + } + /** * If the navigation action * sets `replaceUrl: true` diff --git a/core/README.md b/core/README.md index 1441ec6e5f..cc4ce0015e 100644 --- a/core/README.md +++ b/core/README.md @@ -23,8 +23,8 @@ The Ionic Core package contains the Web Components that make up the reusable UI Easiest way to start using Ionic Core is by adding a script tag to the CDN: ```html - - + + ``` Any Ionic component added to the webpage will automatically load. This includes writing the component tag directly in HTML, or using JavaScript such as `document.createElement('ion-toggle')`. diff --git a/core/package.json b/core/package.json index a9c5949253..5979ca9de1 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/core", - "version": "4.3.0", + "version": "4.3.1", "description": "Base components for Ionic", "keywords": [ "ionic", diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 7a1b044b4c..26b32bffc5 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>` 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. + * 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. + * 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. + * 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 + * 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 + * 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 + * 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 + * 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; } @@ -3728,7 +3728,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>` For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ 'placeholder': string; /** @@ -3818,7 +3818,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>` 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 d3d0937f02..ad3b0abb9e 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'; @@ -72,6 +73,12 @@ 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>` + * + * For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ @Prop() message?: string; @@ -440,7 +447,7 @@ export class Alert implements ComponentInterface, OverlayInterface { {this.subHeader &&

{this.subHeader}

} -
+
{this.renderAlertInputs(labelledById)} {this.renderAlertButtons()} diff --git a/core/src/components/alert/readme.md b/core/src/components/alert/readme.md index ca8afe3668..5744aed888 100644 --- a/core/src/components/alert/readme.md +++ b/core/src/components/alert/readme.md @@ -1055,21 +1055,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>` 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 95d6d71407..b440eb0eb4 100644 --- a/core/src/components/infinite-scroll-content/infinite-scroll-content.tsx +++ b/core/src/components/infinite-scroll-content/infinite-scroll-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-infinite-scroll-content', @@ -22,6 +23,12 @@ 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>` + * + * For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ @Prop() loadingText?: string; @@ -54,7 +61,7 @@ export class InfiniteScrollContent implements ComponentInterface { )} {this.loadingText && ( -
+
)}
); diff --git a/core/src/components/infinite-scroll-content/readme.md b/core/src/components/infinite-scroll-content/readme.md index 04b3649ec8..f0f8d980a1 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>` 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 d936d71b15..2d8c0fef80 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. `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 2cbbd3ff17..8e08642899 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' @@ -17,7 +18,13 @@ export class RefresherContent implements ComponentInterface { @Prop({ mutable: true }) 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. + * `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) */ @Prop() pullingText?: string; @@ -27,7 +34,13 @@ export class RefresherContent implements ComponentInterface { @Prop({ mutable: true }) refreshingSpinner?: SpinnerTypes | null; /** - * The text you want to display when performing a refresh + * 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) */ @Prop() refreshingText?: string; @@ -60,7 +73,7 @@ export class RefresherContent implements ComponentInterface {
} {this.pullingText && -
+
} ,
@@ -70,7 +83,7 @@ export class RefresherContent implements ComponentInterface {
} {this.refreshingText && -
+
} ]; diff --git a/core/src/components/searchbar/readme.md b/core/src/components/searchbar/readme.md index 6a9c167ed1..c86beb046c 100644 --- a/core/src/components/searchbar/readme.md +++ b/core/src/components/searchbar/readme.md @@ -162,23 +162,23 @@ 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` | -| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` | -| `placeholder` | `placeholder` | Set the input's placeholder. | `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` | +| `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 1645a2b2aa..b85569ed9d 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({ @@ -80,6 +81,12 @@ 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>` + * + * For more information: [Security Documentation](https://ionicframework.com/docs/faq/security) */ @Prop() placeholder = 'Search'; @@ -288,7 +295,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 new file mode 100644 index 0000000000..3019250f0d --- /dev/null +++ b/core/src/utils/sanitization/index.ts @@ -0,0 +1,126 @@ +/** + * Does a simple sanitization of all elements + * in an untrusted string + */ + +export const sanitizeDOMString = (untrustedString: string | undefined): string | undefined => { + try { + if (typeof untrustedString !== 'string' || untrustedString === '') { return untrustedString; } + + /** + * Create a document fragment + * separate from the main DOM, + * create a div to do our work in + */ + const documentFragment = document.createDocumentFragment(); + const workingDiv = document.createElement('div'); + documentFragment.appendChild(workingDiv); + workingDiv.innerHTML = untrustedString; + + /** + * Remove any elements + * that are blocked + */ + blockedTags.forEach(blockedTag => { + + const getElementsToRemove = documentFragment.querySelectorAll(blockedTag); + for (let elementIndex = getElementsToRemove.length - 1; elementIndex >= 0; elementIndex--) { + const element = getElementsToRemove[elementIndex]; + if (element.parentNode) { + element.parentNode.removeChild(element); + } else { + documentFragment.removeChild(element); + } + + /** + * We still need to sanitize + * the children of this element + * as they are left behind + */ + const childElements = getElementChildren(element); + + /* tslint:disable-next-line */ + for (let childIndex = 0; childIndex < childElements.length; childIndex++) { + sanitizeElement(childElements[childIndex]); + } + } + }); + + /** + * Go through remaining elements and remove + * non-allowed attribs + */ + + // IE does not support .children on document fragments, only .childNodes + const documentFragmentChildren = getElementChildren(documentFragment); + + /* tslint:disable-next-line */ + for (let childIndex = 0; childIndex < documentFragmentChildren.length; childIndex++) { + sanitizeElement(documentFragmentChildren[childIndex]); + } + + // Append document fragment to div + const fragmentDiv = document.createElement('div'); + fragmentDiv.appendChild(documentFragment); + + // 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); + + return ''; + } +}; + +/** + * 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) => { + // IE uses childNodes, so ignore nodes that are not elements + if (element.nodeType && element.nodeType !== 1) { return; } + + for (let i = element.attributes.length - 1; i >= 0; i--) { + const attribute = element.attributes[i]; + const attributeName = attribute.name; + + // remove non-allowed attribs + if (!allowedAttributes.includes(attributeName.toLowerCase())) { + element.removeAttribute(attributeName); + continue; + } + + // clean up any allowed 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 + */ + const childElements = getElementChildren(element); + + /* tslint:disable-next-line */ + 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; +}; + +const allowedAttributes = ['class', 'id', 'href', 'src']; +const blockedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed']; diff --git a/core/src/utils/sanitization/test/e2e.ts b/core/src/utils/sanitization/test/e2e.ts new file mode 100644 index 0000000000..513946f573 --- /dev/null +++ b/core/src/utils/sanitization/test/e2e.ts @@ -0,0 +1,26 @@ +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'); + 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 new file mode 100644 index 0000000000..fed2f3b7a5 --- /dev/null +++ b/core/src/utils/sanitization/test/index.html @@ -0,0 +1,113 @@ + + + + + Sanitization + + + + + + + + + + + + + + + + Sanitization + + + + +
Results will appear here
+ + Test A + Test B + Test C + Test D + Test E + Test F + Test G + Test H +
+ +
+ + + + + diff --git a/docs/package.json b/docs/package.json index 014e091dfd..d0234bfa15 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/docs", - "version": "4.3.0", + "version": "4.3.1", "description": "Pre-packaged API documentation for the Ionic docs.", "main": "core.json", "files": [