Skip to content

Commit

Permalink
Merge branch 'master' into STRIPES-955
Browse files Browse the repository at this point in the history
  • Loading branch information
zburke authored Jan 7, 2025
2 parents d3567e2 + 8071177 commit 027705f
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 4 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Change history for stripes-template-editor

## IN PROGRESS
* Only change `value` prop to `ReactQuill` if `DOMPurify` made changes. Refs STRIPES-953.
* Export `sanitize` function for module-level value sanitization. Refs STRIPES-953 also.

## [3.4.1](https://github.com/folio-org/stripes-template-editor/tree/v3.4.1) (2024-11-13)
[Full Changelog](https://github.com/folio-org/stripes-template-editor/compare/v3.4.0...v3.4.1)

* Fix `DOMPurify` import. Refs STRIPES-947.

## [3.4.0](https://github.com/folio-org/stripes-template-editor/tree/v3.4.0) (2024-10-15)
[Full Changelog](https://github.com/folio-org/stripes-template-editor/compare/v3.3.4...v3.4.0)

* upgrade `react-quill` version to `2.0.0`. Refs STRIPES-896.
* Push template content through DOMPurify to avoid XSS vulnerabilities. Refs STRIPES-908.
Expand Down
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,40 @@ Version 2.0. See the file "[LICENSE](LICENSE)" for more information.

This is a NPM module to aid with embedding the Quill editor in [Stripes](https://github.com/folio-org/stripes-core/) applications for building templates with token substitution.


## Value Sanitization

In any case where a user-created HTML string will be rendered directly to the UI, it should be sanitized to eliminate any issues with malformed tags/attributes. This library exports a `sanitize` function that should be used within the ui-module prior to passing the value to the form. The function accepts the value to be rendered and an optional overriding configuration for the sanitization library. It will return the sanitized string if any removals were necessary, otherwise it will return the original parameter value.

```
import { sanitize, TemplateEditor } from '@folio/stripes-template-editor'
const value = persistedValue // value obtained from backend...
const appliedValue = sanitize(value);
<Form initialValues={{ template: appliedValue }}>
<Field component="TemplateEditor">
</Form>
```

If the sanitization needs to be adjusted for specific use-cases, it can be imported and extended...

```
import { SANITIZE_CONFIG } from '@folio/stripes-template-editor`;
const localConfig = { ...SANITIZE_CONFIG, ...MY_CONFIG, };
const appliedValue = sanitize(value, localConfig);
```

For the configuration possibilities, reference the [`DOMPurify` configuration details](https://github.com/cure53/DOMPurify?tab=readme-ov-file#can-i-configure-dompurify) if needed!


## Attribution

@skomorokh extracted this from ui-circulation in [this commit](https://github.com/folio-org/ui-circulation/commit/ead94d580d7e0be4e8b9f17d9fc99a2e43fb8cae). The code was largely written by @maximdidenkoepam and @skomorokh probably should have made more of an effort to bring the commit history along. However, you can view it at the originating module.
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { TemplateEditor, PreviewModal, TokensSection, tokensReducer } from './src';
export { TemplateEditor, PreviewModal, TokensSection, tokensReducer, sanitize, SANITIZE_CONFIG } from './src';
2 changes: 1 addition & 1 deletion src/PreviewModal/PreviewModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import Barcode from 'react-barcode';
import HtmlToReact, { Parser } from 'html-to-react';
import { FormattedMessage } from 'react-intl';
import * as DOMPurify from 'dompurify';
import DOMPurify from 'dompurify';

import {
Button,
Expand Down
7 changes: 5 additions & 2 deletions src/TemplateEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import ReactQuill, { Quill } from 'react-quill';
import { v4 as uuidv4 } from 'uuid';
import * as DOMPurify from 'dompurify';

import {
isNull,
forEach,
Expand All @@ -19,6 +19,7 @@ import EditorToolbar from './EditorToolbar';
import PreviewModal from './PreviewModal';
import ControlHeader from './ControlHeader';
import ValidationContainer from './ValidationContainer';
import { sanitize } from './sanitizer';

import tokensReducer from './tokens-reducer';
import IndentStyle from './Attributors/indent';
Expand Down Expand Up @@ -212,6 +213,8 @@ class TemplateEditor extends React.Component {

const invalid = (touched || submitFailed) && !valid && !showTokensDialog;

const appliedValue = sanitize(value);

return (
<>
<Row>
Expand All @@ -228,7 +231,7 @@ class TemplateEditor extends React.Component {
<ReactQuill
id={this.quillId}
className={css.editor}
value={DOMPurify.sanitize(value, { ADD_TAGS: ['Barcode'] })}
value={appliedValue}
ref={this.quill}
modules={this.modules}
onChange={this.onChange}
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as TemplateEditor } from './TemplateEditor';
export { default as PreviewModal } from './PreviewModal';
export { default as TokensSection } from './TokensSection';
export { default as tokensReducer } from './tokens-reducer';
export { sanitize, SANITIZE_CONFIG } from './sanitizer';
16 changes: 16 additions & 0 deletions src/sanitizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import DOMPurify from 'dompurify';

export const SANITIZE_CONFIG = { ADD_TAGS: ['Barcode'], ADD_ATTR: ['target', 'rel'] };

export const sanitize = (value, config = SANITIZE_CONFIG) => {
// since DOMPurify has a known issue of reversing the order of attributes in perfectly admissible HTML
// we check to see if the value was affected - and if not, we just return the unaffected value.
let resultValue = DOMPurify.sanitize(value, config);
if (value !== resultValue) {
const removed = DOMPurify.removed.map((item) => item.attribute?.name || item.element?.outerHTML);
if (removed && removed.length === 0) {
resultValue = value;
}
}
return resultValue;
};

0 comments on commit 027705f

Please sign in to comment.