@ionic/core 8.8.12-dev.11782342444.186b6e9c → 8.8.12-dev.11782931922.18ba507c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/index.js +1 -1
- package/components/ion-action-sheet.js +1 -1
- package/components/ion-alert.js +1 -1
- package/components/ion-infinite-scroll-content.js +1 -1
- package/components/ion-loading.js +1 -1
- package/components/ion-refresher-content.js +1 -1
- package/components/ion-select-modal.js +1 -1
- package/components/ion-select-option.js +1 -1
- package/components/ion-select-popover.js +1 -1
- package/components/ion-select.js +1 -1
- package/components/ion-toast.js +1 -1
- package/components/{p-VlRjwhIK.js → p-279dFMvg.js} +1 -1
- package/components/{p-CWo79Ftj.js → p-C-Rx1zny.js} +1 -1
- package/components/{p-C6x7QJKS.js → p-COdmnpcX.js} +1 -1
- package/components/{p-CgGiBa26.js → p-LMGfIvTf.js} +1 -1
- package/components/{p-DiCu-HwG.js → p-j9Da6cYO.js} +1 -1
- package/components/p-jOtMJMv8.js +4 -0
- package/dist/cjs/{index-C147NqgL.js → index-Cueg3U4w.js} +37 -22
- package/dist/cjs/index.cjs.js +1 -1
- package/dist/cjs/ion-action-sheet.cjs.entry.js +2 -2
- package/dist/cjs/ion-alert.cjs.entry.js +2 -2
- package/dist/cjs/ion-infinite-scroll_2.cjs.entry.js +1 -1
- package/dist/cjs/ion-loading.cjs.entry.js +1 -1
- package/dist/cjs/ion-refresher_2.cjs.entry.js +1 -1
- package/dist/cjs/ion-select-modal.cjs.entry.js +2 -2
- package/dist/cjs/ion-select_3.cjs.entry.js +4 -4
- package/dist/cjs/ion-toast.cjs.entry.js +1 -1
- package/dist/cjs/{select-option-render-BUmJxlqW.js → select-option-render-DTF50t5r.js} +9 -1
- package/dist/collection/components/select-option/select-option.js +3 -3
- package/dist/collection/utils/sanitization/index.js +37 -22
- package/dist/collection/utils/select-option-render.js +8 -0
- package/dist/docs.json +2 -2
- package/dist/esm/{index-CtAGDdQq.js → index-DQSkZyOJ.js} +37 -22
- package/dist/esm/index.js +1 -1
- package/dist/esm/ion-action-sheet.entry.js +2 -2
- package/dist/esm/ion-alert.entry.js +2 -2
- package/dist/esm/ion-infinite-scroll_2.entry.js +1 -1
- package/dist/esm/ion-loading.entry.js +1 -1
- package/dist/esm/ion-refresher_2.entry.js +1 -1
- package/dist/esm/ion-select-modal.entry.js +2 -2
- package/dist/esm/ion-select_3.entry.js +4 -4
- package/dist/esm/ion-toast.entry.js +1 -1
- package/dist/esm/{select-option-render-CZcvbJ--.js → select-option-render-B8nWZRnd.js} +9 -1
- package/dist/html.html-data.json +1 -1
- package/dist/ionic/index.esm.js +1 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/{p-5be4fdc3.entry.js → p-038eb991.entry.js} +1 -1
- package/dist/ionic/{p-1c5a5a59.entry.js → p-4ace7762.entry.js} +1 -1
- package/dist/ionic/{p-c11135d4.entry.js → p-4e9ec5c5.entry.js} +1 -1
- package/dist/ionic/{p-74c540f5.entry.js → p-913b218a.entry.js} +1 -1
- package/dist/ionic/p-BDtODLZ4.js +4 -0
- package/dist/ionic/{p-D3I30-jC.js → p-DCOvXkOm.js} +1 -1
- package/dist/ionic/{p-8e00858b.entry.js → p-a9a33367.entry.js} +1 -1
- package/dist/ionic/{p-c4156e73.entry.js → p-ad728198.entry.js} +1 -1
- package/dist/ionic/{p-0feac19e.entry.js → p-bfc8c629.entry.js} +1 -1
- package/dist/ionic/{p-aedf1108.entry.js → p-c08e666a.entry.js} +1 -1
- package/dist/types/components/select-option/select-option.d.ts +1 -1
- package/dist/types/components.d.ts +2 -2
- package/dist/types/utils/sanitization/index.d.ts +9 -5
- package/hydrate/index.js +47 -24
- package/hydrate/index.mjs +47 -24
- package/package.json +1 -1
- package/components/p-CMpqV7Co.js +0 -4
- package/dist/ionic/p-5x6vmGTf.js +0 -4
|
@@ -9,13 +9,17 @@ var index = require('./index-Ct7gcRif.js');
|
|
|
9
9
|
* Sanitize an untrusted HTML string.
|
|
10
10
|
*
|
|
11
11
|
* Parses the string into a detached DOM, removes blocked tags, strips
|
|
12
|
-
* attributes outside the `
|
|
13
|
-
* and scrubs script-scheme URLs. Returns the sanitized
|
|
12
|
+
* attributes outside the narrow `domStringAllowedAttributes` list (refer
|
|
13
|
+
* `sanitizeElement`), and scrubs script-scheme URLs. Returns the sanitized
|
|
14
|
+
* HTML string.
|
|
14
15
|
*
|
|
15
16
|
* Use this when you have an HTML string from an unknown source and need to
|
|
16
17
|
* render it via `innerHTML`. Use `sanitizeDOMTree` instead when you already
|
|
17
18
|
* have a DOM tree and want to sanitize it in place without a string round
|
|
18
|
-
* trip
|
|
19
|
+
* trip. The two apply the same dangerous-vector scrubbing but different
|
|
20
|
+
* attribute allowlists: this path stays narrow so existing consumers
|
|
21
|
+
* (toast, loading, alert message, etc.) are unaffected, while
|
|
22
|
+
* `sanitizeDOMTree` uses the wider rich-content allowlist.
|
|
19
23
|
*
|
|
20
24
|
* @param untrustedString - The HTML string to sanitize. Pass an
|
|
21
25
|
* `IonicSafeString` to bypass sanitization, or `undefined` to short-circuit.
|
|
@@ -71,7 +75,7 @@ const sanitizeDOMString = (untrustedString) => {
|
|
|
71
75
|
const childElements = getElementChildren(element);
|
|
72
76
|
/* eslint-disable-next-line */
|
|
73
77
|
for (let childIndex = 0; childIndex < childElements.length; childIndex++) {
|
|
74
|
-
sanitizeElement(childElements[childIndex]);
|
|
78
|
+
sanitizeElement(childElements[childIndex], domStringAllowedAttributes);
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
});
|
|
@@ -83,7 +87,7 @@ const sanitizeDOMString = (untrustedString) => {
|
|
|
83
87
|
const dfChildren = getElementChildren(documentFragment);
|
|
84
88
|
/* eslint-disable-next-line */
|
|
85
89
|
for (let childIndex = 0; childIndex < dfChildren.length; childIndex++) {
|
|
86
|
-
sanitizeElement(dfChildren[childIndex]);
|
|
90
|
+
sanitizeElement(dfChildren[childIndex], domStringAllowedAttributes);
|
|
87
91
|
}
|
|
88
92
|
// Append document fragment to div
|
|
89
93
|
const fragmentDiv = document.createElement('div');
|
|
@@ -101,8 +105,8 @@ const sanitizeDOMString = (untrustedString) => {
|
|
|
101
105
|
* Sanitize an entire trusted DOM tree in place.
|
|
102
106
|
*
|
|
103
107
|
* Removes blocked tags (`script`, `iframe`, etc.) from the subtree and
|
|
104
|
-
* then sanitizes attributes on every remaining element using the
|
|
105
|
-
*
|
|
108
|
+
* then sanitizes attributes on every remaining element using the wider
|
|
109
|
+
* `richContentAllowedAttributes` allowlist (refer `sanitizeElement`).
|
|
106
110
|
* Component presentational attributes (`size`, `color`, `shape`, inline
|
|
107
111
|
* SVG, `aria-*`, `data-*`) are preserved; `style`, event handlers (`on*`),
|
|
108
112
|
* form/navigation-hijack attributes, script-scheme URLs, and non-image
|
|
@@ -125,7 +129,7 @@ const sanitizeDOMTree = (root) => {
|
|
|
125
129
|
matches[i].remove();
|
|
126
130
|
}
|
|
127
131
|
});
|
|
128
|
-
sanitizeElement(root);
|
|
132
|
+
sanitizeElement(root, richContentAllowedAttributes, richContentAllowedAttributePrefixes);
|
|
129
133
|
};
|
|
130
134
|
/**
|
|
131
135
|
* Clean up current element based on allowed attributes
|
|
@@ -133,7 +137,7 @@ const sanitizeDOMTree = (root) => {
|
|
|
133
137
|
* clean those up as well
|
|
134
138
|
*/
|
|
135
139
|
// TODO(FW-2832): type (using Element triggers other type errors as well)
|
|
136
|
-
const sanitizeElement = (element) => {
|
|
140
|
+
const sanitizeElement = (element, allowedAttributes, allowedAttributePrefixes = []) => {
|
|
137
141
|
// IE uses childNodes, so ignore nodes that are not elements
|
|
138
142
|
if (element.nodeType && element.nodeType !== 1) {
|
|
139
143
|
return;
|
|
@@ -166,7 +170,7 @@ const sanitizeElement = (element) => {
|
|
|
166
170
|
* (`formaction`, `action`, `target`), namespaced attributes like
|
|
167
171
|
* `xlink:href`, and anything else not explicitly known to be safe.
|
|
168
172
|
*/
|
|
169
|
-
if (!isAttributeAllowed(lowerName)) {
|
|
173
|
+
if (!isAttributeAllowed(lowerName, allowedAttributes, allowedAttributePrefixes)) {
|
|
170
174
|
element.removeAttribute(attributeName);
|
|
171
175
|
continue;
|
|
172
176
|
}
|
|
@@ -209,7 +213,7 @@ const sanitizeElement = (element) => {
|
|
|
209
213
|
const childElements = getElementChildren(element);
|
|
210
214
|
/* eslint-disable-next-line */
|
|
211
215
|
for (let i = 0; i < childElements.length; i++) {
|
|
212
|
-
sanitizeElement(childElements[i]);
|
|
216
|
+
sanitizeElement(childElements[i], allowedAttributes, allowedAttributePrefixes);
|
|
213
217
|
}
|
|
214
218
|
};
|
|
215
219
|
/**
|
|
@@ -269,13 +273,24 @@ const reflectPropertiesToAttributes = (root) => {
|
|
|
269
273
|
}
|
|
270
274
|
};
|
|
271
275
|
/**
|
|
272
|
-
* Attribute
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
+
* Attribute allowlist for `sanitizeDOMString`. Intentionally narrow: this
|
|
277
|
+
* path sanitizes developer HTML strings rendered via `innerHTML` by
|
|
278
|
+
* `ion-toast`, `ion-loading`, the `ion-alert` message,
|
|
279
|
+
* `ion-refresher-content`, and `ion-infinite-scroll-content`, so the list
|
|
280
|
+
* is kept to the minimum those have always needed. Broadening it here would
|
|
281
|
+
* change sanitization output for all of those consumers, so the wider
|
|
282
|
+
* rich-content allowlist below is deliberately scoped to `sanitizeDOMTree`
|
|
283
|
+
* instead.
|
|
284
|
+
*/
|
|
285
|
+
const domStringAllowedAttributes = ['class', 'id', 'href', 'src', 'name', 'slot'];
|
|
286
|
+
/**
|
|
287
|
+
* Attribute allowlist for `sanitizeDOMTree` (the select rich-content path).
|
|
288
|
+
* Covers global HTML attributes, the Ionic component presentational props
|
|
289
|
+
* that cloned rich content (e.g. `ion-select-option` markup) relies on, and
|
|
290
|
+
* the inert SVG presentation attributes used by inline icons.
|
|
276
291
|
*
|
|
277
292
|
* `aria-*` and `data-*` are allowed separately by prefix (refer
|
|
278
|
-
* `
|
|
293
|
+
* `richContentAllowedAttributePrefixes`) since they are inert and not worth
|
|
279
294
|
* enumerating. URL-bearing names (`href`, `src`) are allowed here, but
|
|
280
295
|
* their values are still scrubbed for script schemes in `sanitizeElement`.
|
|
281
296
|
*
|
|
@@ -283,7 +298,7 @@ const reflectPropertiesToAttributes = (root) => {
|
|
|
283
298
|
* form/navigation-hijack attributes (`formaction`, `action`, `target`),
|
|
284
299
|
* which are therefore stripped.
|
|
285
300
|
*/
|
|
286
|
-
const
|
|
301
|
+
const richContentAllowedAttributes = [
|
|
287
302
|
// Global / structural
|
|
288
303
|
'class',
|
|
289
304
|
'id',
|
|
@@ -347,16 +362,16 @@ const allowedAttributes = [
|
|
|
347
362
|
'opacity',
|
|
348
363
|
];
|
|
349
364
|
/**
|
|
350
|
-
* Attribute-name prefixes that are always safe to keep
|
|
351
|
-
* `data-*` attributes cannot execute script or load
|
|
352
|
-
* allowed wholesale rather than enumerated by name.
|
|
365
|
+
* Attribute-name prefixes that are always safe to keep in the rich-content
|
|
366
|
+
* path. `aria-*` and `data-*` attributes cannot execute script or load
|
|
367
|
+
* resources, so they are allowed wholesale rather than enumerated by name.
|
|
353
368
|
*/
|
|
354
|
-
const
|
|
369
|
+
const richContentAllowedAttributePrefixes = ['aria-', 'data-'];
|
|
355
370
|
/**
|
|
356
371
|
* Whether an attribute name (already lowercased) is safe to keep, by exact
|
|
357
372
|
* match or by an allowed prefix.
|
|
358
373
|
*/
|
|
359
|
-
const isAttributeAllowed = (lowerName) => {
|
|
374
|
+
const isAttributeAllowed = (lowerName, allowedAttributes, allowedAttributePrefixes) => {
|
|
360
375
|
if (allowedAttributes.includes(lowerName)) {
|
|
361
376
|
return true;
|
|
362
377
|
}
|
package/dist/cjs/index.cjs.js
CHANGED
|
@@ -12,7 +12,7 @@ var index$1 = require('./index-CAvQ7Tka.js');
|
|
|
12
12
|
var ionicGlobal = require('./ionic-global-B-cA6LkY.js');
|
|
13
13
|
var helpers = require('./helpers-Cv23MFHM.js');
|
|
14
14
|
var index$2 = require('./index-Ct7gcRif.js');
|
|
15
|
-
var index$3 = require('./index-
|
|
15
|
+
var index$3 = require('./index-Cueg3U4w.js');
|
|
16
16
|
var config = require('./config-DWdFbSNK.js');
|
|
17
17
|
var theme = require('./theme-CeDs6Hcv.js');
|
|
18
18
|
var index$4 = require('./index-aC4x3Fk3.js');
|
|
@@ -8,7 +8,7 @@ var buttonActive = require('./button-active-0mPOKmV2.js');
|
|
|
8
8
|
var helpers = require('./helpers-Cv23MFHM.js');
|
|
9
9
|
var lockController = require('./lock-controller-aDB9wrEf.js');
|
|
10
10
|
var overlays = require('./overlays-DSmSrd_4.js');
|
|
11
|
-
var selectOptionRender = require('./select-option-render-
|
|
11
|
+
var selectOptionRender = require('./select-option-render-DTF50t5r.js');
|
|
12
12
|
var theme = require('./theme-CeDs6Hcv.js');
|
|
13
13
|
var ionicGlobal = require('./ionic-global-B-cA6LkY.js');
|
|
14
14
|
var animation = require('./animation-BJq0kcy2.js');
|
|
@@ -19,7 +19,7 @@ require('./index-CAvQ7Tka.js');
|
|
|
19
19
|
require('./gesture-controller-dtqlP_q4.js');
|
|
20
20
|
require('./hardware-back-button-iUuF_76h.js');
|
|
21
21
|
require('./framework-delegate-BtICZDHr.js');
|
|
22
|
-
require('./index-
|
|
22
|
+
require('./index-Cueg3U4w.js');
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* iOS Action Sheet Enter Animation
|
|
@@ -10,8 +10,8 @@ var helpers = require('./helpers-Cv23MFHM.js');
|
|
|
10
10
|
var lockController = require('./lock-controller-aDB9wrEf.js');
|
|
11
11
|
var overlayControlLabel = require('./overlay-control-label-D2sdL2O8.js');
|
|
12
12
|
var overlays = require('./overlays-DSmSrd_4.js');
|
|
13
|
-
var index$1 = require('./index-
|
|
14
|
-
var selectOptionRender = require('./select-option-render-
|
|
13
|
+
var index$1 = require('./index-Cueg3U4w.js');
|
|
14
|
+
var selectOptionRender = require('./select-option-render-DTF50t5r.js');
|
|
15
15
|
var theme = require('./theme-CeDs6Hcv.js');
|
|
16
16
|
var ionicGlobal = require('./ionic-global-B-cA6LkY.js');
|
|
17
17
|
var animation = require('./animation-BJq0kcy2.js');
|
|
@@ -7,7 +7,7 @@ var index = require('./index-Ct7gcRif.js');
|
|
|
7
7
|
var index$1 = require('./index-CIrkNXqJ.js');
|
|
8
8
|
var ionicGlobal = require('./ionic-global-B-cA6LkY.js');
|
|
9
9
|
var config = require('./config-DWdFbSNK.js');
|
|
10
|
-
var index$2 = require('./index-
|
|
10
|
+
var index$2 = require('./index-Cueg3U4w.js');
|
|
11
11
|
require('./helpers-Cv23MFHM.js');
|
|
12
12
|
|
|
13
13
|
const infiniteScrollCss = () => `ion-infinite-scroll{display:none;width:100%}.infinite-scroll-enabled{display:block}`;
|
|
@@ -8,7 +8,7 @@ var config = require('./config-DWdFbSNK.js');
|
|
|
8
8
|
var helpers = require('./helpers-Cv23MFHM.js');
|
|
9
9
|
var lockController = require('./lock-controller-aDB9wrEf.js');
|
|
10
10
|
var overlays = require('./overlays-DSmSrd_4.js');
|
|
11
|
-
var index$1 = require('./index-
|
|
11
|
+
var index$1 = require('./index-Cueg3U4w.js');
|
|
12
12
|
var theme = require('./theme-CeDs6Hcv.js');
|
|
13
13
|
var ionicGlobal = require('./ionic-global-B-cA6LkY.js');
|
|
14
14
|
var animation = require('./animation-BJq0kcy2.js');
|
|
@@ -11,7 +11,7 @@ var haptic = require('./haptic-ClPPQ_PS.js');
|
|
|
11
11
|
var ionicGlobal = require('./ionic-global-B-cA6LkY.js');
|
|
12
12
|
var animation = require('./animation-BJq0kcy2.js');
|
|
13
13
|
var config = require('./config-DWdFbSNK.js');
|
|
14
|
-
var index$3 = require('./index-
|
|
14
|
+
var index$3 = require('./index-Cueg3U4w.js');
|
|
15
15
|
var index$2 = require('./index-DqmRDbxg.js');
|
|
16
16
|
var spinnerConfigs = require('./spinner-configs-DxHKnd3-.js');
|
|
17
17
|
require('./capacitor-DmA66EwP.js');
|
|
@@ -7,14 +7,14 @@ var index = require('./index-Ct7gcRif.js');
|
|
|
7
7
|
var ionicGlobal = require('./ionic-global-B-cA6LkY.js');
|
|
8
8
|
var overlayControlLabel = require('./overlay-control-label-D2sdL2O8.js');
|
|
9
9
|
var overlays = require('./overlays-DSmSrd_4.js');
|
|
10
|
-
var selectOptionRender = require('./select-option-render-
|
|
10
|
+
var selectOptionRender = require('./select-option-render-DTF50t5r.js');
|
|
11
11
|
var theme = require('./theme-CeDs6Hcv.js');
|
|
12
12
|
require('./index-DkNv4J_i.js');
|
|
13
13
|
require('./helpers-Cv23MFHM.js');
|
|
14
14
|
require('./hardware-back-button-iUuF_76h.js');
|
|
15
15
|
require('./framework-delegate-BtICZDHr.js');
|
|
16
16
|
require('./gesture-controller-dtqlP_q4.js');
|
|
17
|
-
require('./index-
|
|
17
|
+
require('./index-Cueg3U4w.js');
|
|
18
18
|
|
|
19
19
|
const ionicSelectModalMdCss = () => `.action-sheet-button-label-has-rich-content.sc-ion-select-modal-ionic,.alert-radio-label-has-rich-content.sc-ion-select-modal-ionic,.alert-checkbox-label-has-rich-content.sc-ion-select-modal-ionic,.select-option-label-has-rich-content.sc-ion-select-modal-ionic{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;gap:16px}.action-sheet-button-label-has-rich-content.sc-ion-select-modal-ionic,.alert-radio-label-has-rich-content.sc-ion-select-modal-ionic,.alert-checkbox-label-has-rich-content.sc-ion-select-modal-ionic,.select-option-content.sc-ion-select-modal-ionic{-ms-flex:1;flex:1}.action-sheet-button-label-text.sc-ion-select-modal-ionic,.alert-checkbox-label-text.sc-ion-select-modal-ionic,.alert-radio-label-text.sc-ion-select-modal-ionic,.select-option-label-text.sc-ion-select-modal-ionic{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;gap:12px}.select-option-start.sc-ion-select-modal-ionic,.select-option-end.sc-ion-select-modal-ionic{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;gap:8px}.select-option-description.sc-ion-select-modal-ionic{padding-left:0;padding-right:0;padding-top:5px;padding-bottom:0;display:block;color:var(--ion-color-step-700, var(--ion-text-color-step-300, #4d4d4d));font-size:0.75rem}.select-option-label.sc-ion-select-modal-ionic:not(.select-option-label-has-rich-content){text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.select-option-label-has-rich-content.sc-ion-select-modal-ionic{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}ion-radio.select-option-has-rich-content.sc-ion-select-modal-ionic::part(label),ion-radio.select-option-has-rich-content.sc-ion-select-modal-ionic [part~="label"],ion-checkbox.select-option-has-rich-content.sc-ion-select-modal-ionic::part(label),ion-checkbox.select-option-has-rich-content.sc-ion-select-modal-ionic [part~="label"],.select-option-content.sc-ion-select-modal-ionic{-ms-flex:1;flex:1;white-space:normal}.select-option-start.sc-ion-select-modal-ionic>ion-avatar.sc-ion-select-modal-ionic,.select-option-end.sc-ion-select-modal-ionic>ion-avatar.sc-ion-select-modal-ionic{width:40px;height:40px}.select-option-start.sc-ion-select-modal-ionic>ion-icon.sc-ion-select-modal-ionic,.select-option-end.sc-ion-select-modal-ionic>ion-icon.sc-ion-select-modal-ionic{width:24px;height:24px}.select-option-start.sc-ion-select-modal-ionic>ion-img.sc-ion-select-modal-ionic,.select-option-start.sc-ion-select-modal-ionic>img.sc-ion-select-modal-ionic,.select-option-start.sc-ion-select-modal-ionic>svg.sc-ion-select-modal-ionic,.select-option-start.sc-ion-select-modal-ionic>ion-thumbnail.sc-ion-select-modal-ionic,.select-option-end.sc-ion-select-modal-ionic>ion-img.sc-ion-select-modal-ionic,.select-option-end.sc-ion-select-modal-ionic>img.sc-ion-select-modal-ionic,.select-option-end.sc-ion-select-modal-ionic>svg.sc-ion-select-modal-ionic,.select-option-end.sc-ion-select-modal-ionic>ion-thumbnail.sc-ion-select-modal-ionic{width:56px;height:56px}.select-option-start.sc-ion-select-modal-ionic>video.sc-ion-select-modal-ionic,.select-option-end.sc-ion-select-modal-ionic>video.sc-ion-select-modal-ionic{width:114px;height:56px}.sc-ion-select-modal-ionic-h{height:100%}ion-list.sc-ion-select-modal-ionic ion-radio.sc-ion-select-modal-ionic::part(container),ion-list.sc-ion-select-modal-ionic ion-radio.sc-ion-select-modal-ionic [part~="container"]{display:none}ion-list.sc-ion-select-modal-ionic ion-radio.sc-ion-select-modal-ionic::part(label),ion-list.sc-ion-select-modal-ionic ion-radio.sc-ion-select-modal-ionic [part~="label"]{margin-left:0;margin-right:0;margin-top:0;margin-bottom:0}ion-item.sc-ion-select-modal-ionic{--inner-border-width:0}.item-radio-checked.sc-ion-select-modal-ionic{--background:rgba(var(--ion-color-primary-rgb, 0, 84, 233), 0.08);--background-focused:var(--ion-color-primary, #0054e9);--background-focused-opacity:0.2;--background-hover:var(--ion-color-primary, #0054e9);--background-hover-opacity:0.12}.item-checkbox-checked.sc-ion-select-modal-ionic{--background-activated:var(--ion-item-color, var(--ion-text-color, #000));--background-focused:var(--ion-item-color, var(--ion-text-color, #000));--background-hover:var(--ion-item-color, var(--ion-text-color, #000));--color:var(--ion-color-primary, #0054e9)}`;
|
|
20
20
|
|
|
@@ -11,13 +11,13 @@ var validity = require('./validity-BpS37YFM.js');
|
|
|
11
11
|
var helpers = require('./helpers-Cv23MFHM.js');
|
|
12
12
|
var overlays = require('./overlays-DSmSrd_4.js');
|
|
13
13
|
var dir = require('./dir-Cn0z1rJH.js');
|
|
14
|
-
var index$2 = require('./index-
|
|
14
|
+
var index$2 = require('./index-Cueg3U4w.js');
|
|
15
15
|
var theme = require('./theme-CeDs6Hcv.js');
|
|
16
16
|
var watchOptions = require('./watch-options-CviOsrTS.js');
|
|
17
17
|
var index$1 = require('./index-DqmRDbxg.js');
|
|
18
18
|
var ionicGlobal = require('./ionic-global-B-cA6LkY.js');
|
|
19
19
|
var overlayControlLabel = require('./overlay-control-label-D2sdL2O8.js');
|
|
20
|
-
var selectOptionRender = require('./select-option-render-
|
|
20
|
+
var selectOptionRender = require('./select-option-render-DTF50t5r.js');
|
|
21
21
|
require('./index-DkNv4J_i.js');
|
|
22
22
|
require('./hardware-back-button-iUuF_76h.js');
|
|
23
23
|
require('./framework-delegate-BtICZDHr.js');
|
|
@@ -1181,12 +1181,12 @@ const SelectOption = class {
|
|
|
1181
1181
|
index.registerInstance(this, hostRef);
|
|
1182
1182
|
this.inputId = `ion-selopt-${selectOptionIds++}`;
|
|
1183
1183
|
/**
|
|
1184
|
-
* If `true`, the user cannot interact with the select option.
|
|
1184
|
+
* If `true`, the user cannot interact with the select option.
|
|
1185
1185
|
*/
|
|
1186
1186
|
this.disabled = false;
|
|
1187
1187
|
}
|
|
1188
1188
|
render() {
|
|
1189
|
-
return index.h(index.Host, { key: '
|
|
1189
|
+
return index.h(index.Host, { key: '8b8bef0f5a55f18048708c34d3ea3ff0ff2f1d47', role: "option", id: this.inputId, class: ionicGlobal.getIonMode(this) });
|
|
1190
1190
|
}
|
|
1191
1191
|
get el() { return index.getElement(this); }
|
|
1192
1192
|
};
|
|
@@ -8,7 +8,7 @@ var config = require('./config-DWdFbSNK.js');
|
|
|
8
8
|
var helpers = require('./helpers-Cv23MFHM.js');
|
|
9
9
|
var lockController = require('./lock-controller-aDB9wrEf.js');
|
|
10
10
|
var overlays = require('./overlays-DSmSrd_4.js');
|
|
11
|
-
var index$3 = require('./index-
|
|
11
|
+
var index$3 = require('./index-Cueg3U4w.js');
|
|
12
12
|
var theme = require('./theme-CeDs6Hcv.js');
|
|
13
13
|
var ionicGlobal = require('./ionic-global-B-cA6LkY.js');
|
|
14
14
|
var animation = require('./animation-BJq0kcy2.js');
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
var index = require('./index-Ct7gcRif.js');
|
|
7
|
-
var index$1 = require('./index-
|
|
7
|
+
var index$1 = require('./index-Cueg3U4w.js');
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Converts a DOM node into a Stencil VNode (or text string) so the
|
|
@@ -75,6 +75,14 @@ const cloneToVNode = (node, keyPrefix, index$1) => {
|
|
|
75
75
|
const renderClonedContent = (id, content, className, useSpan = false) => {
|
|
76
76
|
const Tag = useSpan ? 'span' : 'div';
|
|
77
77
|
const keyPrefix = `${className}-${id}`;
|
|
78
|
+
/**
|
|
79
|
+
* Do not remove. This is the only sanitization pass for callers that pass
|
|
80
|
+
* an `HTMLElement` straight to `renderOptionLabel` (e.g. vanilla JS)
|
|
81
|
+
* without going through `getOptionContent`, which sanitizes upstream.
|
|
82
|
+
* `cloneToVNode` does pure structural conversion and no security
|
|
83
|
+
* filtering, so dropping this call would reopen an XSS hole on the
|
|
84
|
+
* direct `HTMLElement` path.
|
|
85
|
+
*/
|
|
78
86
|
index$1.sanitizeDOMTree(content);
|
|
79
87
|
return (index.h(Tag, { class: className, key: keyPrefix }, Array.from(content.childNodes).map((child, i) => cloneToVNode(child, keyPrefix, i))));
|
|
80
88
|
};
|
|
@@ -14,12 +14,12 @@ export class SelectOption {
|
|
|
14
14
|
constructor() {
|
|
15
15
|
this.inputId = `ion-selopt-${selectOptionIds++}`;
|
|
16
16
|
/**
|
|
17
|
-
* If `true`, the user cannot interact with the select option.
|
|
17
|
+
* If `true`, the user cannot interact with the select option.
|
|
18
18
|
*/
|
|
19
19
|
this.disabled = false;
|
|
20
20
|
}
|
|
21
21
|
render() {
|
|
22
|
-
return h(Host, { key: '
|
|
22
|
+
return h(Host, { key: '8b8bef0f5a55f18048708c34d3ea3ff0ff2f1d47', role: "option", id: this.inputId, class: getIonMode(this) });
|
|
23
23
|
}
|
|
24
24
|
static get is() { return "ion-select-option"; }
|
|
25
25
|
static get encapsulation() { return "shadow"; }
|
|
@@ -47,7 +47,7 @@ export class SelectOption {
|
|
|
47
47
|
"optional": false,
|
|
48
48
|
"docs": {
|
|
49
49
|
"tags": [],
|
|
50
|
-
"text": "If `true`, the user cannot interact with the select option.
|
|
50
|
+
"text": "If `true`, the user cannot interact with the select option."
|
|
51
51
|
},
|
|
52
52
|
"getter": false,
|
|
53
53
|
"setter": false,
|
|
@@ -6,13 +6,17 @@ import { printIonError } from "../logging/index";
|
|
|
6
6
|
* Sanitize an untrusted HTML string.
|
|
7
7
|
*
|
|
8
8
|
* Parses the string into a detached DOM, removes blocked tags, strips
|
|
9
|
-
* attributes outside the `
|
|
10
|
-
* and scrubs script-scheme URLs. Returns the sanitized
|
|
9
|
+
* attributes outside the narrow `domStringAllowedAttributes` list (refer
|
|
10
|
+
* `sanitizeElement`), and scrubs script-scheme URLs. Returns the sanitized
|
|
11
|
+
* HTML string.
|
|
11
12
|
*
|
|
12
13
|
* Use this when you have an HTML string from an unknown source and need to
|
|
13
14
|
* render it via `innerHTML`. Use `sanitizeDOMTree` instead when you already
|
|
14
15
|
* have a DOM tree and want to sanitize it in place without a string round
|
|
15
|
-
* trip
|
|
16
|
+
* trip. The two apply the same dangerous-vector scrubbing but different
|
|
17
|
+
* attribute allowlists: this path stays narrow so existing consumers
|
|
18
|
+
* (toast, loading, alert message, etc.) are unaffected, while
|
|
19
|
+
* `sanitizeDOMTree` uses the wider rich-content allowlist.
|
|
16
20
|
*
|
|
17
21
|
* @param untrustedString - The HTML string to sanitize. Pass an
|
|
18
22
|
* `IonicSafeString` to bypass sanitization, or `undefined` to short-circuit.
|
|
@@ -68,7 +72,7 @@ export const sanitizeDOMString = (untrustedString) => {
|
|
|
68
72
|
const childElements = getElementChildren(element);
|
|
69
73
|
/* eslint-disable-next-line */
|
|
70
74
|
for (let childIndex = 0; childIndex < childElements.length; childIndex++) {
|
|
71
|
-
sanitizeElement(childElements[childIndex]);
|
|
75
|
+
sanitizeElement(childElements[childIndex], domStringAllowedAttributes);
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
78
|
});
|
|
@@ -80,7 +84,7 @@ export const sanitizeDOMString = (untrustedString) => {
|
|
|
80
84
|
const dfChildren = getElementChildren(documentFragment);
|
|
81
85
|
/* eslint-disable-next-line */
|
|
82
86
|
for (let childIndex = 0; childIndex < dfChildren.length; childIndex++) {
|
|
83
|
-
sanitizeElement(dfChildren[childIndex]);
|
|
87
|
+
sanitizeElement(dfChildren[childIndex], domStringAllowedAttributes);
|
|
84
88
|
}
|
|
85
89
|
// Append document fragment to div
|
|
86
90
|
const fragmentDiv = document.createElement('div');
|
|
@@ -98,8 +102,8 @@ export const sanitizeDOMString = (untrustedString) => {
|
|
|
98
102
|
* Sanitize an entire trusted DOM tree in place.
|
|
99
103
|
*
|
|
100
104
|
* Removes blocked tags (`script`, `iframe`, etc.) from the subtree and
|
|
101
|
-
* then sanitizes attributes on every remaining element using the
|
|
102
|
-
*
|
|
105
|
+
* then sanitizes attributes on every remaining element using the wider
|
|
106
|
+
* `richContentAllowedAttributes` allowlist (refer `sanitizeElement`).
|
|
103
107
|
* Component presentational attributes (`size`, `color`, `shape`, inline
|
|
104
108
|
* SVG, `aria-*`, `data-*`) are preserved; `style`, event handlers (`on*`),
|
|
105
109
|
* form/navigation-hijack attributes, script-scheme URLs, and non-image
|
|
@@ -122,7 +126,7 @@ export const sanitizeDOMTree = (root) => {
|
|
|
122
126
|
matches[i].remove();
|
|
123
127
|
}
|
|
124
128
|
});
|
|
125
|
-
sanitizeElement(root);
|
|
129
|
+
sanitizeElement(root, richContentAllowedAttributes, richContentAllowedAttributePrefixes);
|
|
126
130
|
};
|
|
127
131
|
/**
|
|
128
132
|
* Clean up current element based on allowed attributes
|
|
@@ -130,7 +134,7 @@ export const sanitizeDOMTree = (root) => {
|
|
|
130
134
|
* clean those up as well
|
|
131
135
|
*/
|
|
132
136
|
// TODO(FW-2832): type (using Element triggers other type errors as well)
|
|
133
|
-
const sanitizeElement = (element) => {
|
|
137
|
+
const sanitizeElement = (element, allowedAttributes, allowedAttributePrefixes = []) => {
|
|
134
138
|
// IE uses childNodes, so ignore nodes that are not elements
|
|
135
139
|
if (element.nodeType && element.nodeType !== 1) {
|
|
136
140
|
return;
|
|
@@ -163,7 +167,7 @@ const sanitizeElement = (element) => {
|
|
|
163
167
|
* (`formaction`, `action`, `target`), namespaced attributes like
|
|
164
168
|
* `xlink:href`, and anything else not explicitly known to be safe.
|
|
165
169
|
*/
|
|
166
|
-
if (!isAttributeAllowed(lowerName)) {
|
|
170
|
+
if (!isAttributeAllowed(lowerName, allowedAttributes, allowedAttributePrefixes)) {
|
|
167
171
|
element.removeAttribute(attributeName);
|
|
168
172
|
continue;
|
|
169
173
|
}
|
|
@@ -206,7 +210,7 @@ const sanitizeElement = (element) => {
|
|
|
206
210
|
const childElements = getElementChildren(element);
|
|
207
211
|
/* eslint-disable-next-line */
|
|
208
212
|
for (let i = 0; i < childElements.length; i++) {
|
|
209
|
-
sanitizeElement(childElements[i]);
|
|
213
|
+
sanitizeElement(childElements[i], allowedAttributes, allowedAttributePrefixes);
|
|
210
214
|
}
|
|
211
215
|
};
|
|
212
216
|
/**
|
|
@@ -266,13 +270,24 @@ export const reflectPropertiesToAttributes = (root) => {
|
|
|
266
270
|
}
|
|
267
271
|
};
|
|
268
272
|
/**
|
|
269
|
-
* Attribute
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
+
* Attribute allowlist for `sanitizeDOMString`. Intentionally narrow: this
|
|
274
|
+
* path sanitizes developer HTML strings rendered via `innerHTML` by
|
|
275
|
+
* `ion-toast`, `ion-loading`, the `ion-alert` message,
|
|
276
|
+
* `ion-refresher-content`, and `ion-infinite-scroll-content`, so the list
|
|
277
|
+
* is kept to the minimum those have always needed. Broadening it here would
|
|
278
|
+
* change sanitization output for all of those consumers, so the wider
|
|
279
|
+
* rich-content allowlist below is deliberately scoped to `sanitizeDOMTree`
|
|
280
|
+
* instead.
|
|
281
|
+
*/
|
|
282
|
+
const domStringAllowedAttributes = ['class', 'id', 'href', 'src', 'name', 'slot'];
|
|
283
|
+
/**
|
|
284
|
+
* Attribute allowlist for `sanitizeDOMTree` (the select rich-content path).
|
|
285
|
+
* Covers global HTML attributes, the Ionic component presentational props
|
|
286
|
+
* that cloned rich content (e.g. `ion-select-option` markup) relies on, and
|
|
287
|
+
* the inert SVG presentation attributes used by inline icons.
|
|
273
288
|
*
|
|
274
289
|
* `aria-*` and `data-*` are allowed separately by prefix (refer
|
|
275
|
-
* `
|
|
290
|
+
* `richContentAllowedAttributePrefixes`) since they are inert and not worth
|
|
276
291
|
* enumerating. URL-bearing names (`href`, `src`) are allowed here, but
|
|
277
292
|
* their values are still scrubbed for script schemes in `sanitizeElement`.
|
|
278
293
|
*
|
|
@@ -280,7 +295,7 @@ export const reflectPropertiesToAttributes = (root) => {
|
|
|
280
295
|
* form/navigation-hijack attributes (`formaction`, `action`, `target`),
|
|
281
296
|
* which are therefore stripped.
|
|
282
297
|
*/
|
|
283
|
-
const
|
|
298
|
+
const richContentAllowedAttributes = [
|
|
284
299
|
// Global / structural
|
|
285
300
|
'class',
|
|
286
301
|
'id',
|
|
@@ -344,16 +359,16 @@ const allowedAttributes = [
|
|
|
344
359
|
'opacity',
|
|
345
360
|
];
|
|
346
361
|
/**
|
|
347
|
-
* Attribute-name prefixes that are always safe to keep
|
|
348
|
-
* `data-*` attributes cannot execute script or load
|
|
349
|
-
* allowed wholesale rather than enumerated by name.
|
|
362
|
+
* Attribute-name prefixes that are always safe to keep in the rich-content
|
|
363
|
+
* path. `aria-*` and `data-*` attributes cannot execute script or load
|
|
364
|
+
* resources, so they are allowed wholesale rather than enumerated by name.
|
|
350
365
|
*/
|
|
351
|
-
const
|
|
366
|
+
const richContentAllowedAttributePrefixes = ['aria-', 'data-'];
|
|
352
367
|
/**
|
|
353
368
|
* Whether an attribute name (already lowercased) is safe to keep, by exact
|
|
354
369
|
* match or by an allowed prefix.
|
|
355
370
|
*/
|
|
356
|
-
const isAttributeAllowed = (lowerName) => {
|
|
371
|
+
const isAttributeAllowed = (lowerName, allowedAttributes, allowedAttributePrefixes) => {
|
|
357
372
|
if (allowedAttributes.includes(lowerName)) {
|
|
358
373
|
return true;
|
|
359
374
|
}
|
|
@@ -72,6 +72,14 @@ export const cloneToVNode = (node, keyPrefix, index) => {
|
|
|
72
72
|
const renderClonedContent = (id, content, className, useSpan = false) => {
|
|
73
73
|
const Tag = useSpan ? 'span' : 'div';
|
|
74
74
|
const keyPrefix = `${className}-${id}`;
|
|
75
|
+
/**
|
|
76
|
+
* Do not remove. This is the only sanitization pass for callers that pass
|
|
77
|
+
* an `HTMLElement` straight to `renderOptionLabel` (e.g. vanilla JS)
|
|
78
|
+
* without going through `getOptionContent`, which sanitizes upstream.
|
|
79
|
+
* `cloneToVNode` does pure structural conversion and no security
|
|
80
|
+
* filtering, so dropping this call would reopen an XSS hole on the
|
|
81
|
+
* direct `HTMLElement` path.
|
|
82
|
+
*/
|
|
75
83
|
sanitizeDOMTree(content);
|
|
76
84
|
return (h(Tag, { class: className, key: keyPrefix }, Array.from(content.childNodes).map((child, i) => cloneToVNode(child, keyPrefix, i))));
|
|
77
85
|
};
|
package/dist/docs.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"timestamp": "2026-
|
|
2
|
+
"timestamp": "2026-07-01T18:53:53",
|
|
3
3
|
"compiler": {
|
|
4
4
|
"name": "@stencil/core",
|
|
5
5
|
"version": "4.43.5",
|
|
@@ -31885,7 +31885,7 @@
|
|
|
31885
31885
|
"mutable": false,
|
|
31886
31886
|
"attr": "disabled",
|
|
31887
31887
|
"reflectToAttr": false,
|
|
31888
|
-
"docs": "If `true`, the user cannot interact with the select option.
|
|
31888
|
+
"docs": "If `true`, the user cannot interact with the select option.",
|
|
31889
31889
|
"docsTags": [
|
|
31890
31890
|
{
|
|
31891
31891
|
"name": "default",
|