0

I am trying to build a React JS SharePoint modern web part, which have the following capabilities:

  1. Inside the Web Part settings page >> there are 2 fields named as "Who We Are" & "Our Value" which allow the user to enter HTML.

  2. The web part will render 2 buttons "Who We Are" & "Our Value" >> and when the user clicks on any button >> a Popup will be shown with the entered HTML code in step-1.

Something as follow:

enter image description here

But to be able to render HTML code as Rich-Text inside my Web Part, I have to use the dangerouslySetInnerHTML attribute inside the .tsx file, as follow:-

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,
  },
};

so my question is: if using

<p dangerouslySetInnerHTML={{__html:myprops.OurValue}}></p> 

can poses any security issues to render HTML Code inside react.js SharePoint web part?

and if so can I eliminate this security issue? if not, then can I use a safer approach to render HTML code as Rich-Text inside a Popup?

Here is my Full web part code:

inside the 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
                }),
              ]
            }
          ]
        }
      ]
    };
  }
}

inside the 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>
    );
  }
}

1 Answer 1

0

As per the documentation of dangerouslySetInnerHTML,

In general, setting HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) attack. So, you can set HTML directly from React, but you have to type out dangerouslySetInnerHTML and pass an object with a __html key, to remind yourself that it’s dangerous.

So, in general there are security issues related cross site scripting (XSS) using dangerouslySetInnerHTML. But, in your case, I believe you or site admin will be entering HTML in property pane settings & as long as you enter clean HTML there should not be any issues using dangerouslySetInnerHTML.

Also, I see lot of community members are using dangerouslySetInnerHTML in their solutions/samples: sp-dev-fx-webparts samples search.


Also, if XSS is your primary concern, you can use Sanitizer libraries to sanitize your HTML before inserting it in the DOM via dangerouslySetInnerHTML.

You can find more information about this at:

  1. Safe alternative to dangerouslySetInnerHTML
  2. How to prevent XSS attacks when using dangerouslySetInnerHTML in React
5
  • welcome back :) .. now you mentioned But, in your case, I believe you or site admin will be entering HTML in property pane settings & as long as you enter clean HTML there should not be any issues using dangerouslySetInnerHTML... but in my case any user will be able to use the web part inside the modern pages.. i do not think we can lock down the web part to be added by the site admin only.. also if many users are using this inside their solution/samples this does not mean it is safe.. am i correct?
    – John John
    Nov 9, 2021 at 13:29
  • 1
    1. Only users with Contribute/Edit & higher permissions will be able to edit the page > add & configure web part. Limited set of users/content authors should have these permissions & other users should be visitors (read access). 2. I am not saying that this is 100% safe because others are using it >> I just pointed it as it is related to your case (we are also using it in multiple enterprise level web parts without any issues so far) >> Also, I have suggested the solution to sanitize your HTML before adding to DOM if you don't feel it is safe for your environment. Nov 9, 2021 at 13:40
  • in our case all user can write modern pages so they can add the web part i am currently developing... .. also can you specify how i can sanitize the HTML to be used inside the web part in a safer way?
    – John John
    Nov 9, 2021 at 15:07
  • 1
    Check last two links in my answer. There are some libraries which you can use to sanitize the HMTL content. Examples are included in reference links. Nov 9, 2021 at 15:15
  • so what i did >> i install this inside my project npm install dompurify eslint-plugin-risxss >> then i added the following import to my MyModal.tsx import { sanitize } from 'dompurify'; >> then i render the HTML using this code <div dangerouslySetInnerHTML={{ __html: sanitize(myprops.WhoWeAre) }} /> .. so i am safe now?
    – John John
    Nov 9, 2021 at 15:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.