From a2aac2ecbbeb6fc90b3f40acfdef830000ddcc52 Mon Sep 17 00:00:00 2001 From: chuhuangvio25 Date: Tue, 1 Sep 2026 22:08:25 +0800 Subject: [PATCH] fix(sanitization): match onload case- and space-insensitively sanitizeDOMString blocked untrusted HTML that contains "onload=" before setting it via innerHTML, because onload can fire synchronously while parsing into the detached document fragment, ahead of the later attribute-allowlist pass. The check used a plain lowercase substring match, so variants like onLoad=, ONLOAD= or onload = (whitespace before the =) were not caught, even though HTML parses them as the same event handler. Use a case-insensitive regex that also tolerates whitespace around the =. Adds a test covering the case and whitespace variants. --- core/src/utils/sanitization/index.ts | 6 +++++- .../utils/sanitization/test/sanitization.spec.ts | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index bb851fea7e0..2ee633f4a6f 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -36,8 +36,12 @@ export const sanitizeDOMString = (untrustedString: IonicSafeString | string | un * fragment in Chrome. If a string * contains onload then we should not * attempt to add this to the fragment. + * + * HTML attribute names are case-insensitive and may have whitespace + * around the `=`, so match those variants too (e.g. `onLoad=`, + * `ONLOAD =`) instead of only the exact lowercase `onload=` substring. */ - if (untrustedString.includes('onload=')) { + if (/onload\s*=/i.test(untrustedString)) { return ''; } diff --git a/core/src/utils/sanitization/test/sanitization.spec.ts b/core/src/utils/sanitization/test/sanitization.spec.ts index 2ca069e387f..03c2abf7c48 100644 --- a/core/src/utils/sanitization/test/sanitization.spec.ts +++ b/core/src/utils/sanitization/test/sanitization.spec.ts @@ -26,6 +26,21 @@ describe('sanitizeDOMString', () => { ).toEqual(''); }); + it('filter onload regardless of case or whitespace around =', () => { + /** + * onload is blocked with an upfront string check (rather than the + * attribute-stripping pass used for onerror/onclick above) because it + * can fire synchronously while the untrusted string is being parsed + * into the working document fragment, before that pass runs. HTML + * attribute names are case-insensitive and may have whitespace around + * `=`, so the check must not be a plain lowercase substring match. + */ + expect(sanitizeDOMString('')).toEqual(''); + expect(sanitizeDOMString('')).toEqual(''); + expect(sanitizeDOMString('')).toEqual(''); + expect(sanitizeDOMString('')).toEqual(''); + }); + it('filter href JS', () => { expect(sanitizeDOMString('harmless link')).toEqual( 'harmless link'