{"id":"GHSA-5jcr-82fh-339v","summary":"Cross-Site-Scripting attack on `\u003cRichTextField\u003e`","details":"### Impact\n\nAll React applications built with react-admin and using the `\u003cRichTextField\u003e` are affected. \n\n`\u003cRichTextField\u003e` outputs the field value using `dangerouslySetInnerHTML` without client-side sanitization. If the data isn't sanitized server-side, this opens a possible Cross-Site-Scripting (XSS) attack. \n\nProof of concept:\n\n```jsx\nimport { RichTextField } from 'react-admin';\n\nconst record = {\n    id: 1,\n    body: `\n\u003cp\u003e\n\u003cstrong\u003eWar and Peace\u003c/strong\u003e is a novel by the Russian author\n\u003ca href=\"https://en.wikipedia.org/wiki/Leo_Tolstoy\" onclick=\"document.getElementById('stolendata').value='credentials';\"\u003eLeo Tolstoy\u003c/a\u003e,\npublished serially, then in its entirety in 1869.\n\u003c/p\u003e\n\u003cp onmouseover=\"document.getElementById('stolendata').value='credentials';\"\u003e\nIt is regarded as one of Tolstoy's finest literary achievements and remains a classic of world literature.\n\u003c/p\u003e\n\u003cimg src=\"x\" onerror=\"document.getElementById('stolendata').value='credentials';\" /\u003e\n`,\n};\n\nconst VulnerableRichTextField = () =\u003e (\n    \u003c\u003e\n        \u003cRichTextField record={record} source=\"body\" /\u003e\n        \u003chr /\u003e\n        \u003ch4\u003eStolen data:\u003c/h4\u003e\n        \u003cinput id=\"stolendata\" defaultValue=\"none\" /\u003e\n    \u003c/\u003e\n);\n```\n\n### Patches\n\nVersions 3.19.12 and 4.7.6 now use `DOMPurify` to escape the HTML before outputting it with React and `dangerouslySetInnerHTML`\n\n### Workarounds\n\nYou don't need to upgrade if you already sanitize HTML data server-side. \n\nOtherwise, you'll have to replace the `\u003cRichTextField\u003e` by a custom field doing sanitization by hand:\n\n```tsx\n// react-admin v4\nimport * as React from 'react';\nimport { memo } from 'react';\nimport PropTypes from 'prop-types';\nimport get from 'lodash/get';\nimport Typography from '@material-ui/core/Typography';\nimport { useRecordContext, sanitizeFieldRestProps, fieldPropTypes } from 'react-admin';\nimport purify from 'dompurify';\n\nexport const removeTags = (input) =\u003e\n    input ? input.replace(/\u003c[^\u003e]+\u003e/gm, '') : '';\n\nconst RichTextField = memo(\n    props =\u003e {\n        const { className, emptyText, source, stripTags, ...rest } = props;\n        const record = useRecordContext(props);\n        const value = get(record, source);\n\n        return (\n            \u003cTypography\n                className={className}\n                variant=\"body2\"\n                component=\"span\"\n                {...sanitizeFieldRestProps(rest)}\n            \u003e\n                {value == null && emptyText ? (\n                    emptyText\n                ) : stripTags ? (\n                    removeTags(value)\n                ) : (\n                    \u003cspan\n                        dangerouslySetInnerHTML={{\n                            __html: purify.sanitize(value),\n                        }}\n                    /\u003e\n                )}\n            \u003c/Typography\u003e\n        );\n    }\n);\n\nRichTextField.defaultProps = {\n    addLabel: true,\n    stripTags: false,\n};\n\nRichTextField.propTypes = {\n    // @ts-ignore\n    ...Typography.propTypes,\n    ...fieldPropTypes,\n    stripTags: PropTypes.bool,\n};\n\nRichTextField.displayName = 'RichTextField';\n\nexport default RichTextField;\n```\n\n### References\n\nhttps://github.com/marmelab/react-admin/pull/8644, https://github.com/marmelab/react-admin/pull/8645\n","aliases":["CVE-2023-25572"],"modified":"2026-09-10T03:49:56.699898587Z","published":"2023-02-14T00:32:21Z","database_specific":{"nvd_published_at":"2023-02-13T21:15:00Z","cwe_ids":["CWE-79"],"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2023-02-14T00:32:21Z"},"references":[{"type":"WEB","url":"https://github.com/marmelab/react-admin/security/advisories/GHSA-5jcr-82fh-339v"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2023-25572"},{"type":"WEB","url":"https://github.com/marmelab/react-admin/pull/8644"},{"type":"WEB","url":"https://github.com/marmelab/react-admin/pull/8645"},{"type":"PACKAGE","url":"https://github.com/marmelab/react-admin"},{"type":"WEB","url":"https://github.com/marmelab/react-admin/releases/tag/v3.19.12"},{"type":"WEB","url":"https://github.com/marmelab/react-admin/releases/tag/v4.7.6"}],"affected":[{"package":{"name":"react-admin","ecosystem":"npm","purl":"pkg:npm/react-admin"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"3.19.12"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-5jcr-82fh-339v/GHSA-5jcr-82fh-339v.json"}},{"package":{"name":"react-admin","ecosystem":"npm","purl":"pkg:npm/react-admin"},"ranges":[{"type":"SEMVER","events":[{"introduced":"4.0.0"},{"fixed":"4.7.6"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-5jcr-82fh-339v/GHSA-5jcr-82fh-339v.json"}},{"package":{"name":"ra-ui-materialui","ecosystem":"npm","purl":"pkg:npm/ra-ui-materialui"},"ranges":[{"type":"SEMVER","events":[{"introduced":"4.0.0"},{"fixed":"4.7.6"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-5jcr-82fh-339v/GHSA-5jcr-82fh-339v.json"}},{"package":{"name":"ra-ui-materialui","ecosystem":"npm","purl":"pkg:npm/ra-ui-materialui"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"3.19.12"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/02/GHSA-5jcr-82fh-339v/GHSA-5jcr-82fh-339v.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N"}]}