diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index 70532791a..abb4ef40a 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -9,7 +9,7 @@ jobs:
strategy:
matrix:
- node-version: [20]
+ node-version: [22]
fail-fast: true
steps:
diff --git a/assets/lang/strings.ts b/assets/lang/strings.ts
index 0c6ccf585..0e6929e5e 100644
--- a/assets/lang/strings.ts
+++ b/assets/lang/strings.ts
@@ -375,6 +375,8 @@ const translations = {
},
openMailboxes: 'Open mailboxes',
unableToDecryptPreview: 'This message could not be decrypted',
+ showRemoteImages: 'Show images',
+ remoteImagesBlocked: 'Images in this message are blocked',
},
compose_email: {
title: 'New message',
@@ -1433,6 +1435,8 @@ const translations = {
},
openMailboxes: 'Abrir bandejas',
unableToDecryptPreview: 'No se ha podido descifrar este mensaje',
+ showRemoteImages: 'Mostrar imágenes',
+ remoteImagesBlocked: 'Las imágenes de este mensaje están bloqueadas',
},
compose_email: {
title: 'Nuevo mensaje',
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 5213ea119..a1921ade7 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1609,8 +1609,6 @@ PODS:
- Yoga
- react-native-randombytes (3.6.2):
- React-Core
- - react-native-render-html (6.3.4):
- - React-Core
- react-native-safe-area-context (5.6.2):
- hermes-engine
- RCTRequired
@@ -2549,7 +2547,6 @@ DEPENDENCIES:
- react-native-pdf (from `../node_modules/react-native-pdf`)
- react-native-pdf-thumbnail (from `../node_modules/react-native-pdf-thumbnail`)
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
- - react-native-render-html (from `../node_modules/react-native-render-html`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- react-native-video (from `../node_modules/react-native-video`)
- react-native-webview (from `../node_modules/react-native-webview`)
@@ -2772,8 +2769,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-pdf-thumbnail"
react-native-randombytes:
:path: "../node_modules/react-native-randombytes"
- react-native-render-html:
- :path: "../node_modules/react-native-render-html"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-video:
@@ -2955,7 +2950,6 @@ SPEC CHECKSUMS:
react-native-pdf: 70836b3b48595c7f85a3aa2b5d122b175c455733
react-native-pdf-thumbnail: cc2076c2d871e43cf267611975db8ed3682cc65e
react-native-randombytes: 4eeddad82d1fd57adda1cd5beffe67d041c9d6de
- react-native-render-html: 5afc4751f1a98621b3009432ef84c47019dcb2bd
react-native-safe-area-context: 37e680fc4cace3c0030ee46e8987d24f5d3bdab2
react-native-video: f5982e21efab0dc356d92541a8a9e19581307f58
react-native-webview: b29007f4723bca10872028067b07abacfa1cb35a
diff --git a/jest.config.ts b/jest.config.ts
index 1fda8fb6a..737479ac8 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -24,6 +24,13 @@ const untranspiledModulePatterns = [
'p-limit',
'yocto-queue',
'mime',
+ 'sanitize-html',
+ 'htmlparser2',
+ 'domhandler',
+ 'domutils',
+ 'dom-serializer',
+ 'domelementtype',
+ 'entities',
];
const config: Config.InitialOptions = {
diff --git a/package.json b/package.json
index 7a4ca9b4e..9cd669184 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
"@shopify/flash-list": "2.0.2",
"@testing-library/react-hooks": "^8.0.1",
"@types/luxon": "^3.0.1",
+ "@types/sanitize-html": "^2.16.1",
"@types/unorm": "^1.3.28",
"async": "^3.2.6",
"axios": "^1.18.1",
@@ -113,7 +114,6 @@
"react-native-progress": "^5.0.0",
"react-native-randombytes": "^3.6.2",
"react-native-reanimated": "~4.1.1",
- "react-native-render-html": "6.3.4",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-share": "^7.3.3",
@@ -132,6 +132,7 @@
"redux": "^4.0.5",
"reflect-metadata": "^0.2.2",
"regenerator-runtime": "^0.14.1",
+ "sanitize-html": "^2.17.7",
"socket.io-client": "^4.8.3",
"stream-browserify": "^3.0.0",
"tailwind-rn": "^4.2.0",
@@ -179,7 +180,7 @@
"typescript": "~5.9.2"
},
"engines": {
- "node": ">=20"
+ "node": ">=22.12.0"
},
"resolutions": {
"pbkdf2": "^3.1.3",
diff --git a/src/screens/mail/EmailDetailScreen/EmailBody.tsx b/src/screens/mail/EmailDetailScreen/EmailBody.tsx
new file mode 100644
index 000000000..996789f25
--- /dev/null
+++ b/src/screens/mail/EmailDetailScreen/EmailBody.tsx
@@ -0,0 +1,115 @@
+import { EmailResponse } from '@internxt/sdk/dist/mail/types';
+import { useEffect, useMemo, useRef, useState } from 'react';
+import { Linking, TouchableOpacity, View } from 'react-native';
+import { WebView } from 'react-native-webview';
+import type { ShouldStartLoadRequest, WebViewMessageEvent } from 'react-native-webview/lib/WebViewTypes';
+import { useTailwind } from 'tailwind-rn';
+
+import { logger } from '@internxt-mobile/services/common/logger/logger.service';
+import strings from '../../../../assets/lang/strings';
+import AppText from '../../../components/AppText';
+import useGetColor from '../../../hooks/useColor';
+import {
+ buildEmailBodyHtml,
+ hasRemoteImages,
+ type EmailBodySource,
+} from '../../../services/mail/emailBody/emailBodyContent';
+import { buildEmailDocument } from '../../../services/mail/emailBody/emailDocument';
+import { useEmailBodyHeight } from './hooks/useEmailBodyHeight';
+
+const OPENABLE_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:']);
+const GENERIC_USER_AGENT = 'Mozilla/5.0 (Mobile)';
+
+export const EmailBody = ({ message, bodySource }: { message: EmailResponse; bodySource: EmailBodySource }) => {
+ const tailwind = useTailwind();
+ const getColor = useGetColor();
+ const [areRemoteImagesAllowed, setAreRemoteImagesAllowed] = useState(false);
+ const isFirstLoad = useRef(true);
+ const backgroundColor = getColor('bg-surface');
+ const textColor = getColor('text-gray-100');
+
+ const body = useMemo(() => {
+ const html = buildEmailBodyHtml(message, bodySource);
+ return { html, hasImagesHostedElsewhere: hasRemoteImages(html) };
+ }, [message, bodySource]);
+
+ const emailDocument = useMemo(
+ () => buildEmailDocument(body.html, { backgroundColor, textColor, areRemoteImagesAllowed }),
+ [body.html, backgroundColor, textColor, areRemoteImagesAllowed],
+ );
+
+ const { height, heightReporterScript, onHeightReported, onMeasureFailed } = useEmailBodyHeight(emailDocument);
+
+ useEffect(() => {
+ isFirstLoad.current = true;
+ }, [emailDocument]);
+
+ const onMessageFromBody = (event: WebViewMessageEvent) => {
+ onHeightReported(event.nativeEvent.data);
+ };
+
+ const onNavigationRequested = (request: ShouldStartLoadRequest) => {
+ if (isFirstLoad.current || request.url === 'about:blank') {
+ isFirstLoad.current = false;
+ return true;
+ }
+
+ const scheme = request.url.slice(0, request.url.indexOf(':') + 1).toLowerCase();
+ if (OPENABLE_SCHEMES.has(scheme)) {
+ Linking.openURL(request.url).catch((error) => logger.error('Could not open a link from an email:', error));
+ }
+ return false;
+ };
+
+ const areImagesBlocked = !areRemoteImagesAllowed && body.hasImagesHostedElsewhere;
+
+ return (
+
An unreadable copy
', textBody: 'an encrypted payload' }); + + expect(resolveEmailBody(message, { type: 'encryptedUnreadable' })).toEqual({ + content: '', + isHtml: false, + }); + }); + + test('when a message has no body at all, then nothing is displayed', () => { + const message = anEmail({ htmlBody: null, textBody: null }); + + expect(resolveEmailBody(message, { type: 'plain' })).toEqual({ + content: '', + isHtml: false, + }); + }); +}); + +describe('Displaying a message written as plain text', () => { + test('when the text spans several lines, then the line breaks are kept', () => { + const html = plainTextToHtml('First line\nSecond line'); + + expect(html).toContain('white-space:pre-wrap'); + expect(html).toContain('First line\nSecond line'); + }); + + test('when the text contains characters that look like markup, then they are shown as written', () => { + const html = plainTextToHtml('a < b && c > d'); + + expect(html).toContain('a < b && c > d'); + }); + + test('when the text has apostrophes and quotation marks, then they are shown without anything added around them', () => { + const html = plainTextToHtml('it\'s the "big" one, isn\'t it?'); + + expect(html).toContain('it's the "big" one, isn't it?'); + }); + + test('when the text looks like a script, then it is shown as text instead of being rendered', () => { + const html = plainTextToHtml(''); + + expect(html).not.toContain('' }); + + const bodyHtml = buildEmailBodyHtml(message, { type: 'plain' }); + + expect(bodyHtml).toContain('Hello
'); + }); + + test('when a message body contains a link with a javascript scheme, then the link is removed', () => { + const clean = sanitizeMailHtml('Click here'); + + expect(clean).not.toContain('javascript'); + expect(clean).toContain('Click here'); + }); + + test('when a message body contains an inline event handler, then the attribute is stripped', () => { + const clean = sanitizeMailHtml('Before
'); + + expect(clean).toBe('Before
'); + }); + + test('when a message body is empty, then nothing is returned', () => { + expect(sanitizeMailHtml('')).toBe(''); + }); +}); diff --git a/src/services/mail/emailBody/sanitizeMailHtml.ts b/src/services/mail/emailBody/sanitizeMailHtml.ts new file mode 100644 index 000000000..2b6daa1ba --- /dev/null +++ b/src/services/mail/emailBody/sanitizeMailHtml.ts @@ -0,0 +1,21 @@ +import sanitizeHtml from 'sanitize-html'; + +const MAIL_SANITISE_OPTIONS: sanitizeHtml.IOptions = { + allowedTags: [...sanitizeHtml.defaults.allowedTags, 'img'], + allowedAttributes: { + ...sanitizeHtml.defaults.allowedAttributes, + '*': ['style'], + }, + allowedSchemes: ['http', 'https', 'mailto', 'tel', 'cid'], + allowProtocolRelative: false, +}; + +/** + * Filters the body of an incoming email down to the markup that is safe to display: it keeps the + * formatting and the inline attachment references, and drops scripts, event handlers and any link + * whose scheme is not http, https, mailto, tel or cid. + * + * @param html the body of the email as it arrived, before rendering it + * @returns the same body with everything unsafe removed + */ +export const sanitizeMailHtml = (html: string): string => sanitizeHtml(html, MAIL_SANITISE_OPTIONS); diff --git a/yarn.lock b/yarn.lock index 8e3f71275..b9add733d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2134,38 +2134,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@jsamr/counter-style@^2.0.1": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@jsamr/counter-style/-/counter-style-2.0.2.tgz#6f08cfa98e1f0416dc1d7f2d8ac38a8cdb004c5d" - integrity sha512-2mXudGVtSzVxWEA7B9jZLKjoXUeUFYDDtFrQoC0IFX9/Dszz4t1vZOmafi3JSw/FxD+udMQ+4TAFR8Qs0J3URQ== - -"@jsamr/react-native-li@^2.3.0": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@jsamr/react-native-li/-/react-native-li-2.3.1.tgz#12a5b5f6e3971cec77b96bee58104eed0ae9314a" - integrity sha512-Qbo4NEj48SQ4k8FZJHFE2fgZDKTWaUGmVxcIQh3msg5JezLdTMMHuRRDYctfdHI6L0FZGObmEv3haWbIvmol8w== - -"@native-html/css-processor@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@native-html/css-processor/-/css-processor-1.11.0.tgz#27d02e5123b0849f4986d44060ba3f235a15f552" - integrity sha512-NnhBEbJX5M2gBGltPKOetiLlKhNf3OHdRafc8//e2ZQxXN8JaSW/Hy8cm94pnIckQxwaMKxrtaNT3x4ZcffoNQ== - dependencies: - css-to-react-native "^3.0.0" - csstype "^3.0.8" - -"@native-html/transient-render-engine@11.2.3": - version "11.2.3" - resolved "https://registry.yarnpkg.com/@native-html/transient-render-engine/-/transient-render-engine-11.2.3.tgz#e4de0e7c8c023224a2dc27f3bd2b30d3984d94a4" - integrity sha512-zXwgA3gPUEmFs3I3syfnvDvS6WiUHXEE6jY09OBzK+trq7wkweOSFWIoyXiGkbXrozGYG0KY90YgPyr8Tg8Uyg== - dependencies: - "@native-html/css-processor" "1.11.0" - "@types/ramda" "^0.27.44" - csstype "^3.0.9" - domelementtype "^2.2.0" - domhandler "^4.2.2" - domutils "^2.8.0" - htmlparser2 "^7.1.2" - ramda "^0.27.2" - "@noble/ciphers@^2.2.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-2.4.0.tgz#04cf9e0f1cd3d521e8e5ca92dc0c02238180f676" @@ -2816,13 +2784,6 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== -"@types/ramda@^0.27.40", "@types/ramda@^0.27.44": - version "0.27.66" - resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.27.66.tgz#f1a23d13b0087d806a62e3ff941e5e59b3318999" - integrity sha512-i2YW+E2U6NfMt3dp0RxNcejox+bxJUNDjB7BpYuRuoHIzv5juPHkJkNgcUOu+YSQEmaWu8cnAo/8r63C0NnuVA== - dependencies: - ts-toolbelt "^6.15.1" - "@types/react-native-video@^5.0.14": version "5.0.21" resolved "https://registry.yarnpkg.com/@types/react-native-video/-/react-native-video-5.0.21.tgz#3d7be0002907ff44e54a478712cfcd0768ce6edb" @@ -2848,6 +2809,13 @@ "@types/prop-types" "*" csstype "^3.2.2" +"@types/sanitize-html@^2.16.1": + version "2.16.1" + resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.16.1.tgz#27b9ac6cc29838f7a048bfec0113e8ad00918d0a" + integrity sha512-n9wjs8bCOTyN/ynwD8s/nTcTreIHB1vf31vhLMGqUPNHaweKC4/fAl4Dj+hUlCTKYgm4P3k83fmiFfzkZ6sgMA== + dependencies: + htmlparser2 "^10.1" + "@types/semver@^7.3.12": version "7.7.1" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" @@ -2868,11 +2836,6 @@ resolved "https://registry.yarnpkg.com/@types/unorm/-/unorm-1.3.31.tgz#c93166bb937d5763e374857e1b57847c6c653a4d" integrity sha512-qCPX/Lo14ECb9Wkb/1sxdcTQqIiHTVNlaHczGrh2WqMVSlWjfn8Hu7DxraCtBYz1+Ud6Id/d+4OH/hkd+dlnpw== -"@types/urijs@^1.19.15": - version "1.19.26" - resolved "https://registry.yarnpkg.com/@types/urijs/-/urijs-1.19.26.tgz#500fc9912e0ba01d635480970bdc9ba0f45d7bc6" - integrity sha512-wkXrVzX5yoqLnndOwFsieJA7oKM8cNkOKJtf/3vVGSUFkWDKZvFHpIl9Pvqb/T9UsawBBFMTTD8xu7sK5MWuvg== - "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -3784,16 +3747,6 @@ char-regex@^2.0.0: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-2.0.2.tgz#81385bb071af4df774bff8721d0ca15ef29ea0bb" integrity sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg== -character-entities-html4@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" - integrity sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g== - -character-entities-legacy@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" - integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== - child-process-promise@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/child-process-promise/-/child-process-promise-2.2.1.tgz#4730a11ef610fad450b8f223c79d31d7bdad8074" @@ -4247,7 +4200,7 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -csstype@^3.0.8, csstype@^3.0.9, csstype@^3.2.2: +csstype@^3.2.2: version "3.2.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== @@ -4266,6 +4219,11 @@ dayjs@^1.11.19: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.19.tgz#15dc98e854bb43917f12021806af897c58ae2938" integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== +dayjs@^1.11.7: + version "1.11.23" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.23.tgz#b0a363506dde5f36cf5075e42ebe8115165a8c79" + integrity sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ== + debug@2.6.9, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -4473,15 +4431,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-serializer@^1.0.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" - integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" - dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -4491,11 +4440,25 @@ dom-serializer@^2.0.0: domhandler "^5.0.2" entities "^4.2.0" -domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: +dom-serializer@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-3.1.1.tgz#54be70ee4fcc2da010f488165e62294798dc57d3" + integrity sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw== + dependencies: + domelementtype "^3.0.0" + domhandler "^6.0.0" + entities "^8.0.0" + +domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== +domelementtype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-3.0.0.tgz#e6c0a24bd39ca5eb7ea67a98d2f699497d7bcc55" + integrity sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg== + domexception@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" @@ -4503,13 +4466,6 @@ domexception@^4.0.0: dependencies: webidl-conversions "^7.0.0" -domhandler@^4.2.0, domhandler@^4.2.2: - version "4.3.1" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" - integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== - dependencies: - domelementtype "^2.2.0" - domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" @@ -4517,16 +4473,14 @@ domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" -domutils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== +domhandler@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-6.0.1.tgz#75f351a03a6e10c35e08418f9cf0d287e00797cf" + integrity sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg== dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" + domelementtype "^3.0.0" -domutils@^3.0.1: +domutils@^3.0.1, domutils@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== @@ -4535,6 +4489,15 @@ domutils@^3.0.1: domelementtype "^2.3.0" domhandler "^5.0.3" +domutils@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-4.0.2.tgz#0c1ac1fdbe8f554a60a6f5eb143ee85630327c94" + integrity sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA== + dependencies: + dom-serializer "^3.0.0" + domelementtype "^3.0.0" + domhandler "^6.0.0" + dot-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -4635,16 +4598,6 @@ engine.io-parser@~5.2.1: resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f" integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q== -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - -entities@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" - integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== - entities@^4.2.0, entities@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" @@ -4655,6 +4608,16 @@ entities@^6.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== +entities@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-7.0.1.tgz#26e8a88889db63417dcb9a1e79a3f1bc92b5976b" + integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA== + +entities@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-8.1.0.tgz#9632d69619ebdbe0cf0ef22f53088fe5a2163d86" + integrity sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA== + env-editor@^0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/env-editor/-/env-editor-0.4.2.tgz#4e76568d0bd8f5c2b6d314a9412c8fe9aa3ae861" @@ -5726,15 +5689,25 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -htmlparser2@^7.1.2: - version "7.2.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.2.0.tgz#8817cdea38bbc324392a90b1990908e81a65f5a5" - integrity sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog== +htmlparser2@^10.1: + version "10.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-10.1.0.tgz#fe3f2e12c73b6e462d4e10395db9c1119e4d6ae4" + integrity sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ== dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.2" - domutils "^2.8.0" - entities "^3.0.1" + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.2.2" + entities "^7.0.1" + +htmlparser2@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-12.0.0.tgz#6a679d0f57c525990f9cbad8a585b320ecc6d198" + integrity sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw== + dependencies: + domelementtype "^3.0.0" + domhandler "^6.0.0" + domutils "^4.0.2" + entities "^8.0.0" http-errors@~2.0.1: version "2.0.1" @@ -5980,6 +5953,11 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-plain-object@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.1.0.tgz#22ed7b5e856bb57966348e5b9468dada42be87f4" + integrity sha512-bUi/yjmtKYcRVUtWRGr0UA6xEFh2I6zWUwMrUXB3s7bmYCaZ8a+0ZsTRkrawh/mzlSD1Y0Ph8bp/U+TvBpWDNw== + is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" @@ -6654,6 +6632,13 @@ lan-network@^0.2.1: resolved "https://registry.yarnpkg.com/lan-network/-/lan-network-0.2.1.tgz#e4764a0d17f6bd1f2794c838fa219526a1b756f8" integrity sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A== +launder@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/launder/-/launder-1.7.1.tgz#ef7155ab0c3ddec2323089c961d2e9a249aa5b0d" + integrity sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw== + dependencies: + dayjs "^1.11.7" + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -7643,6 +7628,11 @@ parse-png@^2.1.0: dependencies: pngjs "^3.3.0" +parse-srcset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" + integrity sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q== + parse5@^7.0.0, parse5@^7.1.1: version "7.3.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" @@ -7852,7 +7842,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.4.47, postcss@^8.5.23, postcss@~8.4.32: +postcss@^8.3.11, postcss@^8.4.47, postcss@^8.5.23, postcss@~8.4.32: version "8.5.23" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.23.tgz#3493550116f478487298301d2c2e8dc5a56e6594" integrity sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg== @@ -7942,7 +7932,7 @@ prompts@^2.0.1, prompts@^2.2.1, prompts@^2.3.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@*, prop-types@15.8.1, prop-types@^15.5.7, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@*, prop-types@15.8.1, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8041,11 +8031,6 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -ramda@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.2.tgz#84463226f7f36dc33592f6f4ed6374c48306c3f1" - integrity sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA== - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -8261,21 +8246,6 @@ react-native-reanimated@~4.1.1: react-native-is-edge-to-edge "^1.2.1" semver "7.7.2" -react-native-render-html@6.3.4: - version "6.3.4" - resolved "https://registry.yarnpkg.com/react-native-render-html/-/react-native-render-html-6.3.4.tgz#01684897bed2de84829e540a1dbb3a7bdf9d0e57" - integrity sha512-H2jSMzZjidE+Wo3qCWPUMU1nm98Vs2SGCvQCz/i6xf0P3Y9uVtG/b0sDbG/cYFir2mSYBYCIlS1Dv0WC1LjYig== - dependencies: - "@jsamr/counter-style" "^2.0.1" - "@jsamr/react-native-li" "^2.3.0" - "@native-html/transient-render-engine" "11.2.3" - "@types/ramda" "^0.27.40" - "@types/urijs" "^1.19.15" - prop-types "^15.5.7" - ramda "^0.27.2" - stringify-entities "^3.1.0" - urijs "^1.19.6" - react-native-safe-area-context@~5.6.0: version "5.6.2" resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz#283e006f5b434fb247fcb4be0971ad7473d5c560" @@ -8777,6 +8747,19 @@ sanitize-filename@^1.6.1: dependencies: truncate-utf8-bytes "^1.0.0" +sanitize-html@^2.17.7: + version "2.17.7" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.17.7.tgz#b16417c348ea5f99c2451964b6e6621ca6b1da94" + integrity sha512-PGtEkc9cbnedU3s9TmzDbpsZ8w086g/0Q8k8/oIO1NLNU3i5k9yn835CrjJSajp1KMmkisbO1qPXxNKO3welAg== + dependencies: + deepmerge "^4.2.2" + escape-string-regexp "^4.0.0" + htmlparser2 "^12.0.0" + is-plain-object "^5.0.0" + launder "^1.7.1" + parse-srcset "^1.0.2" + postcss "^8.3.11" + sax@>=0.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/sax/-/sax-1.6.0.tgz#da59637629307b97e7c4cb28e080a7bc38560d5b" @@ -9200,15 +9183,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-entities@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-3.1.0.tgz#b8d3feac256d9ffcc9fa1fefdcf3ca70576ee903" - integrity sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg== - dependencies: - character-entities-html4 "^1.0.0" - character-entities-legacy "^1.0.0" - xtend "^4.0.0" - strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -9597,11 +9571,6 @@ ts-object-utils@0.0.5: resolved "https://registry.yarnpkg.com/ts-object-utils/-/ts-object-utils-0.0.5.tgz#95361cdecd7e52167cfc5e634c76345e90a26077" integrity sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA== -ts-toolbelt@^6.15.1: - version "6.15.5" - resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz#cb3b43ed725cb63644782c64fbcad7d8f28c0a83" - integrity sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A== - tslib@2.8.1, tslib@^2.0.3, tslib@^2.1.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" @@ -9773,11 +9742,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urijs@^1.19.6: - version "1.19.11" - resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.11.tgz#204b0d6b605ae80bea54bea39280cdb7c9f923cc" - integrity sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ== - url-parse@^1.5.3: version "1.5.10" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" @@ -10097,11 +10061,6 @@ xmlhttprequest-ssl@~2.1.1: resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz#e9e8023b3f29ef34b97a859f584c5e6c61418e23" integrity sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ== -xtend@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"