在 React SharePoint WebPart 中,使用 'html-react-parser' 和使用 'dompurify eslint-plugin-risxss' 安全显示 HTML 之间有什么区别
In React SharePoint WebPart what are the differences between using 'html-react-parser' & using 'dompurify eslint-plugin-risxss' to securely show HTML
我正在尝试构建一个 React.js SharePoint 新式 Web 部件,它具有以下功能:-
在 Web 部件设置页面内 >> 有 2 个名为“我们是谁”和“我们的价值”的字段,允许用户输入 HTML.
Web 部件将呈现 2 个按钮“我们是谁”和“我们的价值”>> 并且当用户单击任何按钮时 >> 将显示一个弹出窗口,其中包含输入的 HTML 步骤 1 中的代码
内容如下:-
但是为了能够在我的 Web 部件中将 HTML 代码呈现为 Rich-Text,我必须在 .tsx 文件中使用 dangerouslySetInnerHTML
属性。如下:-
import * as React from 'react';
import { useId, useBoolean } from '@fluentui/react-hooks';
import {
getTheme,
mergeStyleSets,
FontWeights,
Modal,
IIconProps,
IStackProps,
} from '@fluentui/react';
import { IconButton, IButtonStyles } from '@fluentui/react/lib/Button';
export const MYModal2 = (myprops) => {
const [isModalOpen, { setTrue: showModal, setFalse: hideModal }] = useBoolean(false);
const [isPopup, setisPopup] = React.useState(true);
const titleId = useId('title');
React.useEffect(() => {
showModal();
}, [isPopup]);
function ExitHandler() {
hideModal();
setisPopup(current => !current)
myprops.handler();
}
return (
<div>
<Modal
titleAriaId={titleId}
isOpen={isModalOpen}
onDismiss={ExitHandler}
isBlocking={true}
containerClassName={contentStyles.container}
>
<div className={contentStyles.header}>
<span id={titleId}>Modal Popup</span>
<IconButton
styles={iconButtonStyles}
iconProps={cancelIcon}
ariaLabel="Close popup modal"
onClick={ExitHandler}
/>
</div>
<div className={contentStyles.body}>
<p dangerouslySetInnerHTML={{__html:myprops.OurValue}}>
</p>
</div>
</Modal>
</div>
);
};
const cancelIcon: IIconProps = { iconName: 'Cancel' };
const theme = getTheme();
const contentStyles = mergeStyleSets({
container: {
display: 'flex',
flexFlow: 'column nowrap',
alignItems: 'stretch',
},
header: [
// eslint-disable-next-line deprecation/deprecation
theme.fonts.xLarge,
{
flex: '1 1 auto',
borderTop: '4px solid ${theme.palette.themePrimary}',
color: theme.palette.neutralPrimary,
display: 'flex',
alignItems: 'center',
fontWeight: FontWeights.semibold,
padding: '12px 12px 14px 24px',
},
],
body: {
flex: '4 4 auto',
padding: '0 24px 24px 24px',
overflowY: 'hidden',
selectors: {
p: { margin: '14px 0' },
'p:first-child': { marginTop: 0 },
'p:last-child': { marginBottom: 0 },
},
},
});
const stackProps: Partial<IStackProps> = {
horizontal: true,
tokens: { childrenGap: 40 },
styles: { root: { marginBottom: 20 } },
};
const iconButtonStyles: Partial<IButtonStyles> = {
root: {
color: theme.palette.neutralPrimary,
marginLeft: 'auto',
marginTop: '4px',
marginRight: '2px',
},
rootHovered: {
color: theme.palette.neutralDark,
},
};
为了保护 dangerouslySetInnerHTML
,我执行了以下步骤:-
1- 在我的 Node.Js CMD 中 >> i 运行 我的项目目录中的这个命令:-
npm install dompurify eslint-plugin-risxss
2- 然后在我上面的 .tsx
我做了以下修改:-
- 我添加了这个导入
import { sanitize } from 'dompurify';
- 我用这个
<div dangerouslySetInnerHTML={{ __html: sanitize(myprops.OurValue) }} />
替换了这个不安全的代码 <p dangerouslySetInnerHTML={{__html:myprops.OurValue}}></p>
所以我有以下问题:-
- 现在我的方法(使用
sanitize(myprops.OurValue)
will/should 安全地将 HTML 呈现为弹出窗口中的富文本,因为我使用的是 sanitize
函数,它是dompurify eslint-plugin-risxss
。但我读了另一种方法,其中提到要在弹出窗口中安全地将 HTML 呈现为 Rich-Text,我们可以使用 html-react-parser
包,如下所示 {parse(myprops.OurValue)}
。所以使用 'html-react-parser' 和使用 'dompurify eslint-plugin-risxss' 将 HTML 代码安全地呈现为 React Web 部件弹出窗口中的 Rich-Text 有什么区别?
这是我的完整 web 部件代码:-
里面MyModalPopupWebPart.ts:-
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'MyModalPopupWebPartStrings';
import MyModalPopup from './components/MyModalPopup';
import { IMyModalPopupProps } from './components/IMyModalPopupProps';
export interface IMyModalPopupWebPartProps {
description: string;
WhoWeAre: string;
OurValue:string;
}
export default class MyModalPopupWebPart extends BaseClientSideWebPart<IMyModalPopupWebPartProps> {
public render(): void {
const element: React.ReactElement<IMyModalPopupProps> = React.createElement(
MyModalPopup,
{
description: this.properties.description,
WhoWeAre: this.properties.WhoWeAre,
OurValue: this.properties.OurValue
}
);
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('WhoWeAre', {
label: "who We Are",
multiline: true
}),
PropertyPaneTextField('OurValue', {
label: "Our value"
}), PropertyPaneTextField('description', {
label: "Description",
multiline: true
}),
]
}
]
}
]
};
}
}
里面MyModalPopup.tsx:-
import * as React from 'react';
import { IMyModalPopupProps } from './IMyModalPopupProps';
import { DefaultButton } from '@fluentui/react/lib/Button';
import { MYModal } from './MYModal';
import { MYModal2 } from './MYModal2';
interface IPopupState {
showModal: string;
}
export default class MyModalPopup extends React.Component<IMyModalPopupProps, IPopupState> {
constructor(props: IMyModalPopupProps, state: IPopupState) {
super(props);
this.state = {
showModal: ''
};
this.handler = this.handler.bind(this);
this.Buttonclick = this.Buttonclick.bind(this);
}
handler() {
this.setState({
showModal: ''
})
}
private Buttonclick(e, whichModal) {
e.preventDefault();
this.setState({ showModal: whichModal });
}
public render(): React.ReactElement<IMyModalPopupProps> {
const { showModal } = this.state;
return (
<div>
<DefaultButton onClick={(e) => this.Buttonclick(e, 'our-value')} text="Our Value" />
{ showModal === 'our-value' && <MYModal2 OurValue={this.props.OurValue} myprops={this.state} handler={this.handler} />}
<DefaultButton onClick={(e) => this.Buttonclick(e, 'who-we-are')} text="Who We Are" />
{ showModal === 'who-we-are' && <MYModal WhoWeAre={this.props.WhoWeAre} myprops={this.state} handler={this.handler} />}
</div>
);
}
}
实际上,html-react-parser
returns ReactJs 对象,它的 return 类型类似于 React.createElement
或类似称为 JSX 的类型。
使用 DOMPurify.sanitize
will return safe pure HTML elements which those are different to the object that html-react-parser
returns. the risxss
ESLint plugin will force you to use sanitizing with any kind of sanitize function or library, that I left an answer to your 如何清理 字符串 HTML.
最终,使用消毒更好,因为 html-react-parser
会将您的 字符串 HTML 转换为 ReactJs有一些微小变化的对象会很危险,因为项目中可能有一些 string HTML 的脚本,它可能有害,只需删除 onclick
或 onload
,等等,来自 HTML 标签,但清理会删除所有可能有害的标签。清理也会收到配置,这意味着您可以拥有自己的 选项 来进行清理。
我正在尝试构建一个 React.js SharePoint 新式 Web 部件,它具有以下功能:-
在 Web 部件设置页面内 >> 有 2 个名为“我们是谁”和“我们的价值”的字段,允许用户输入 HTML.
Web 部件将呈现 2 个按钮“我们是谁”和“我们的价值”>> 并且当用户单击任何按钮时 >> 将显示一个弹出窗口,其中包含输入的 HTML 步骤 1 中的代码
内容如下:-
但是为了能够在我的 Web 部件中将 HTML 代码呈现为 Rich-Text,我必须在 .tsx 文件中使用 dangerouslySetInnerHTML
属性。如下:-
import * as React from 'react';
import { useId, useBoolean } from '@fluentui/react-hooks';
import {
getTheme,
mergeStyleSets,
FontWeights,
Modal,
IIconProps,
IStackProps,
} from '@fluentui/react';
import { IconButton, IButtonStyles } from '@fluentui/react/lib/Button';
export const MYModal2 = (myprops) => {
const [isModalOpen, { setTrue: showModal, setFalse: hideModal }] = useBoolean(false);
const [isPopup, setisPopup] = React.useState(true);
const titleId = useId('title');
React.useEffect(() => {
showModal();
}, [isPopup]);
function ExitHandler() {
hideModal();
setisPopup(current => !current)
myprops.handler();
}
return (
<div>
<Modal
titleAriaId={titleId}
isOpen={isModalOpen}
onDismiss={ExitHandler}
isBlocking={true}
containerClassName={contentStyles.container}
>
<div className={contentStyles.header}>
<span id={titleId}>Modal Popup</span>
<IconButton
styles={iconButtonStyles}
iconProps={cancelIcon}
ariaLabel="Close popup modal"
onClick={ExitHandler}
/>
</div>
<div className={contentStyles.body}>
<p dangerouslySetInnerHTML={{__html:myprops.OurValue}}>
</p>
</div>
</Modal>
</div>
);
};
const cancelIcon: IIconProps = { iconName: 'Cancel' };
const theme = getTheme();
const contentStyles = mergeStyleSets({
container: {
display: 'flex',
flexFlow: 'column nowrap',
alignItems: 'stretch',
},
header: [
// eslint-disable-next-line deprecation/deprecation
theme.fonts.xLarge,
{
flex: '1 1 auto',
borderTop: '4px solid ${theme.palette.themePrimary}',
color: theme.palette.neutralPrimary,
display: 'flex',
alignItems: 'center',
fontWeight: FontWeights.semibold,
padding: '12px 12px 14px 24px',
},
],
body: {
flex: '4 4 auto',
padding: '0 24px 24px 24px',
overflowY: 'hidden',
selectors: {
p: { margin: '14px 0' },
'p:first-child': { marginTop: 0 },
'p:last-child': { marginBottom: 0 },
},
},
});
const stackProps: Partial<IStackProps> = {
horizontal: true,
tokens: { childrenGap: 40 },
styles: { root: { marginBottom: 20 } },
};
const iconButtonStyles: Partial<IButtonStyles> = {
root: {
color: theme.palette.neutralPrimary,
marginLeft: 'auto',
marginTop: '4px',
marginRight: '2px',
},
rootHovered: {
color: theme.palette.neutralDark,
},
};
为了保护 dangerouslySetInnerHTML
,我执行了以下步骤:-
1- 在我的 Node.Js CMD 中 >> i 运行 我的项目目录中的这个命令:-
npm install dompurify eslint-plugin-risxss
2- 然后在我上面的 .tsx
我做了以下修改:-
- 我添加了这个导入
import { sanitize } from 'dompurify';
- 我用这个
<div dangerouslySetInnerHTML={{ __html: sanitize(myprops.OurValue) }} />
替换了这个不安全的代码
<p dangerouslySetInnerHTML={{__html:myprops.OurValue}}></p>
所以我有以下问题:-
- 现在我的方法(使用
sanitize(myprops.OurValue)
will/should 安全地将 HTML 呈现为弹出窗口中的富文本,因为我使用的是sanitize
函数,它是dompurify eslint-plugin-risxss
。但我读了另一种方法,其中提到要在弹出窗口中安全地将 HTML 呈现为 Rich-Text,我们可以使用html-react-parser
包,如下所示{parse(myprops.OurValue)}
。所以使用 'html-react-parser' 和使用 'dompurify eslint-plugin-risxss' 将 HTML 代码安全地呈现为 React Web 部件弹出窗口中的 Rich-Text 有什么区别?
这是我的完整 web 部件代码:-
里面MyModalPopupWebPart.ts:-
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'MyModalPopupWebPartStrings';
import MyModalPopup from './components/MyModalPopup';
import { IMyModalPopupProps } from './components/IMyModalPopupProps';
export interface IMyModalPopupWebPartProps {
description: string;
WhoWeAre: string;
OurValue:string;
}
export default class MyModalPopupWebPart extends BaseClientSideWebPart<IMyModalPopupWebPartProps> {
public render(): void {
const element: React.ReactElement<IMyModalPopupProps> = React.createElement(
MyModalPopup,
{
description: this.properties.description,
WhoWeAre: this.properties.WhoWeAre,
OurValue: this.properties.OurValue
}
);
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('WhoWeAre', {
label: "who We Are",
multiline: true
}),
PropertyPaneTextField('OurValue', {
label: "Our value"
}), PropertyPaneTextField('description', {
label: "Description",
multiline: true
}),
]
}
]
}
]
};
}
}
里面MyModalPopup.tsx:-
import * as React from 'react';
import { IMyModalPopupProps } from './IMyModalPopupProps';
import { DefaultButton } from '@fluentui/react/lib/Button';
import { MYModal } from './MYModal';
import { MYModal2 } from './MYModal2';
interface IPopupState {
showModal: string;
}
export default class MyModalPopup extends React.Component<IMyModalPopupProps, IPopupState> {
constructor(props: IMyModalPopupProps, state: IPopupState) {
super(props);
this.state = {
showModal: ''
};
this.handler = this.handler.bind(this);
this.Buttonclick = this.Buttonclick.bind(this);
}
handler() {
this.setState({
showModal: ''
})
}
private Buttonclick(e, whichModal) {
e.preventDefault();
this.setState({ showModal: whichModal });
}
public render(): React.ReactElement<IMyModalPopupProps> {
const { showModal } = this.state;
return (
<div>
<DefaultButton onClick={(e) => this.Buttonclick(e, 'our-value')} text="Our Value" />
{ showModal === 'our-value' && <MYModal2 OurValue={this.props.OurValue} myprops={this.state} handler={this.handler} />}
<DefaultButton onClick={(e) => this.Buttonclick(e, 'who-we-are')} text="Who We Are" />
{ showModal === 'who-we-are' && <MYModal WhoWeAre={this.props.WhoWeAre} myprops={this.state} handler={this.handler} />}
</div>
);
}
}
实际上,html-react-parser
returns ReactJs 对象,它的 return 类型类似于 React.createElement
或类似称为 JSX 的类型。
使用 DOMPurify.sanitize
will return safe pure HTML elements which those are different to the object that html-react-parser
returns. the risxss
ESLint plugin will force you to use sanitizing with any kind of sanitize function or library, that I left an answer to your
最终,使用消毒更好,因为 html-react-parser
会将您的 字符串 HTML 转换为 ReactJs有一些微小变化的对象会很危险,因为项目中可能有一些 string HTML 的脚本,它可能有害,只需删除 onclick
或 onload
,等等,来自 HTML 标签,但清理会删除所有可能有害的标签。清理也会收到配置,这意味着您可以拥有自己的 选项 来进行清理。