GHSA-5JCR-82FH-339V
Vulnerability from github – Published: 2023-02-14 00:32 – Updated: 2023-02-14 00:32
VLAI
Summary
Cross-Site-Scripting attack on `<RichTextField>`
Details
Impact
All React applications built with react-admin and using the <RichTextField> are affected.
<RichTextField> 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.
Proof of concept:
import { RichTextField } from 'react-admin';
const record = {
id: 1,
body: `
<p>
<strong>War and Peace</strong> is a novel by the Russian author
<a href="https://en.wikipedia.org/wiki/Leo_Tolstoy" onclick="document.getElementById('stolendata').value='credentials';">Leo Tolstoy</a>,
published serially, then in its entirety in 1869.
</p>
<p onmouseover="document.getElementById('stolendata').value='credentials';">
It is regarded as one of Tolstoy's finest literary achievements and remains a classic of world literature.
</p>
<img src="x" onerror="document.getElementById('stolendata').value='credentials';" />
`,
};
const VulnerableRichTextField = () => (
<>
<RichTextField record={record} source="body" />
<hr />
<h4>Stolen data:</h4>
<input id="stolendata" defaultValue="none" />
</>
);
Patches
Versions 3.19.12 and 4.7.6 now use DOMPurify to escape the HTML before outputting it with React and dangerouslySetInnerHTML
Workarounds
You don't need to upgrade if you already sanitize HTML data server-side.
Otherwise, you'll have to replace the <RichTextField> by a custom field doing sanitization by hand:
// react-admin v4
import * as React from 'react';
import { memo } from 'react';
import PropTypes from 'prop-types';
import get from 'lodash/get';
import Typography from '@material-ui/core/Typography';
import { useRecordContext, sanitizeFieldRestProps, fieldPropTypes } from 'react-admin';
import purify from 'dompurify';
export const removeTags = (input) =>
input ? input.replace(/<[^>]+>/gm, '') : '';
const RichTextField = memo(
props => {
const { className, emptyText, source, stripTags, ...rest } = props;
const record = useRecordContext(props);
const value = get(record, source);
return (
<Typography
className={className}
variant="body2"
component="span"
{...sanitizeFieldRestProps(rest)}
>
{value == null && emptyText ? (
emptyText
) : stripTags ? (
removeTags(value)
) : (
<span
dangerouslySetInnerHTML={{
__html: purify.sanitize(value),
}}
/>
)}
</Typography>
);
}
);
RichTextField.defaultProps = {
addLabel: true,
stripTags: false,
};
RichTextField.propTypes = {
// @ts-ignore
...Typography.propTypes,
...fieldPropTypes,
stripTags: PropTypes.bool,
};
RichTextField.displayName = 'RichTextField';
export default RichTextField;
References
https://github.com/marmelab/react-admin/pull/8644, https://github.com/marmelab/react-admin/pull/8645
Severity
5.4 (Medium)
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "react-admin"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.19.12"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "react-admin"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.7.6"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "ra-ui-materialui"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.7.6"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "ra-ui-materialui"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.19.12"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-25572"
],
"database_specific": {
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2023-02-14T00:32:21Z",
"nvd_published_at": "2023-02-13T21:15:00Z",
"severity": "MODERATE"
},
"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\u0027t sanitized server-side, this opens a possible Cross-Site-Scripting (XSS) attack. \n\nProof of concept:\n\n```jsx\nimport { RichTextField } from \u0027react-admin\u0027;\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(\u0027stolendata\u0027).value=\u0027credentials\u0027;\"\u003eLeo Tolstoy\u003c/a\u003e,\npublished serially, then in its entirety in 1869.\n\u003c/p\u003e\n\u003cp onmouseover=\"document.getElementById(\u0027stolendata\u0027).value=\u0027credentials\u0027;\"\u003e\nIt is regarded as one of Tolstoy\u0027s finest literary achievements and remains a classic of world literature.\n\u003c/p\u003e\n\u003cimg src=\"x\" onerror=\"document.getElementById(\u0027stolendata\u0027).value=\u0027credentials\u0027;\" /\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\u0027t need to upgrade if you already sanitize HTML data server-side. \n\nOtherwise, you\u0027ll 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 \u0027react\u0027;\nimport { memo } from \u0027react\u0027;\nimport PropTypes from \u0027prop-types\u0027;\nimport get from \u0027lodash/get\u0027;\nimport Typography from \u0027@material-ui/core/Typography\u0027;\nimport { useRecordContext, sanitizeFieldRestProps, fieldPropTypes } from \u0027react-admin\u0027;\nimport purify from \u0027dompurify\u0027;\n\nexport const removeTags = (input) =\u003e\n input ? input.replace(/\u003c[^\u003e]+\u003e/gm, \u0027\u0027) : \u0027\u0027;\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 \u0026\u0026 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 = \u0027RichTextField\u0027;\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",
"id": "GHSA-5jcr-82fh-339v",
"modified": "2023-02-14T00:32:21Z",
"published": "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"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Cross-Site-Scripting attack on `\u003cRichTextField\u003e`"
}
Loading…
Loading…
Experimental. This forecast is provided for visualization only and may change without notice. Do not use it for operational decisions.
Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Loading…
Loading…