Skip to content

Commit

Permalink
feat!: make DOMPurify as optional (#1195)
Browse files Browse the repository at this point in the history
* feat!: make DOMPurify as optional
- replace `isomorphic-dompurify` with `dompurify` and make it a dev deps

* chore: fix failing unit tests
  • Loading branch information
ghiscoding committed May 7, 2024
1 parent 58831dd commit 5a84ff8
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 68 deletions.
3 changes: 0 additions & 3 deletions docs/column-functionalities/editors.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,6 @@ this.columnDefinitions = [
];
```
### Change Default DOMPurify Options (sanitize html)
If you find that the HTML that you passed is being sanitized and you wish to change it, then you can change the default `sanitizeHtmlOptions` property defined in the Global Grid Options, for more info on how to change these global options, see the [Wiki - Global Grid Options](../grid-functionalities/global-options.md) and also take a look at the [GitHub - DOMPurify](https://github.com/cure53/DOMPurify#can-i-configure-it) configurations.
## Editor Options
#### Column Editor `editorOptions`
Expand Down
3 changes: 0 additions & 3 deletions docs/column-functionalities/editors/select-dropdown-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,3 @@ this.columnDefinitions = [
}
];
```
### Change Default DOMPurify Options (sanitize html)
If you find that the HTML that you passed is being sanitized and you wish to change it, then you can change the default `sanitizeHtmlOptions` property defined in the Global Grid Options, for more info on how to change these global options, see the `Global Grid Options` and also take a look at the [GitHub - DOMPurify](https://github.com/cure53/DOMPurify#can-i-configure-it) configurations.
4 changes: 0 additions & 4 deletions docs/column-functionalities/filters/select-filter.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,6 @@ this.columnDefinitions = [
];
```

#### Change Default DOMPurify Options (sanitize html)
If you find that the HTML that you passed is being sanitized and you wish to change it, then you can change the default `sanitizeHtmlOptions` property defined in the Global Grid Options, for more info on how to change these global options, see the [Wiki - Global Grid Options](../../grid-functionalities/Global-Options.md) and also take a look at the [GitHub - DOMPurify](https://github.com/cure53/DOMPurify#can-i-configure-it) configurations.


### Collection Add Blank Entry
In some cases a blank entry at the beginning of the collection could be useful, the most common example for this is to use the first option as a blank entry to tell our Filter to show everything. So for that we can use the `addBlankEntry` flag in `collectionOptions
Expand Down
2 changes: 1 addition & 1 deletion docs/column-functionalities/formatters.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ Since version 7.x, you can now also return native DOM element instead of an HTML
2. Performance (the reasons are similar to point 1.)
- since it's native it can be appended directly to the grid cell
- when it's an HTML string, it has to do 2 extra steps (which is an overhead process)
i. sanitize the string (we use [DOMPurify](https://github.com/cure53/DOMPurify) by default)
i. sanitize the string (when a sanitizer, for example [DOMPurify](https://github.com/cure53/DOMPurify))
ii. SlickGrid then has to convert it to native element by using `innerHTML` on the grid cell

Demo
Expand Down
39 changes: 23 additions & 16 deletions docs/developer-guides/csp-compliance.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
## CSP Compliance
The library is now, at least mostly, CSP (Content Security Policy) compliant since `v4.0`, however there are some exceptions to be aware of. When using any html string as template (for example with Custom Formatter returning an html string), you will not be fully compliant unless you return `TrustedHTML`. You can achieve this by using the `sanitizer` method in combo with [DOMPurify](https://github.com/cure53/DOMPurify) to return `TrustedHTML` as shown below and with that in place you should be CSP compliant.
The library is for the most part CSP (Content Security Policy) compliant since `v4.0` **but** only if you configure the `sanitizer` grid option. We were previously using `DOMPurify` internally in the project (in version <=4.x) but it was made optional in version 5 and higher. The main reason to make it optional was because most users would use `dompurify` but some users who require SSR support would want to use `isomorphic-dompurify`. You could also skip the `sanitizer` configuration, but that is not recommended.

> **Note** the default sanitizer in Slickgrid-Universal is actually already configured to return `TrustedHTML` but the CSP safe in the DataView is opt-in via `useCSPSafeFilter`
> **Note** even if the `sanitizer` is optional, we **strongly suggest** that you configure it as a global grid option to avoid possible XSS attacks from your data and also to be CSP compliant. Note that for Salesforce users, you do not have to configure it since Salesforce already use DOMPurify internally.
```typescript
import DOMPurify from 'dompurify';
import { GridOption } from 'aurelia-slickgrid';
As mentioned above, the project is mostly CSP compliant, however there are some exceptions to be aware of. When using any html string as template (for example with Custom Formatter returning an html string), you will not be fully compliant unless you return `TrustedHTML`. You can achieve this by using the `sanitizer` method in combo with [DOMPurify](https://github.com/cure53/DOMPurify) to return `TrustedHTML` as shown below and with that in place you should be CSP compliant.

export class Example1 {
gridOptions: GridOption;
```ts
// prefer the global grid options if possible
this.gridOptions = {
sanitizer: (dirtyHtml) => DOMPurify.sanitize(dirtyHtml, { ADD_ATTR: ['level'], RETURN_TRUSTED_TYPE: true })
};
```

prepareGrid() {
// ...
> **Note** If you're wondering about the `ADD_ATTR: ['level']`, well the "level" is a custom attribute used by SlickGrid Grouping/Draggable Grouping to track the grouping level depth and it must be kept.
this.gridOptions = {
// NOTE: DOM Purify is already configured in Slickgrid-Universal with the configuration shown below
sanitizer: (html) => DOMPurify.sanitize(html, { RETURN_TRUSTED_TYPE: true }),
// you could also optionally use the sanitizerOptions instead
// sanitizerOptions: { RETURN_TRUSTED_TYPE: true }
}
}
> **Note** the DataView is not CSP safe by default, it is opt-in via the `useCSPSafeFilter` option.
```typescript
import DOMPurify from 'dompurify';
import { Slicker, SlickVanillaGridBundle } from '@slickgrid-universal/vanilla-bundle';

// DOM Purify is already configured in Slickgrid-Universal with the configuration shown below
this.gridOptions = {
sanitizer: (html) => DOMPurify.sanitize(html, { RETURN_TRUSTED_TYPE: true }),
// you could also optionally use the sanitizerOptions instead
// sanitizerOptions: { RETURN_TRUSTED_TYPE: true }
}
this.sgb = new Slicker.GridBundle(gridContainerElm, this.columnDefinitions, this.gridOptions, this.dataset);
```

with this code in place, we can use the following CSP meta tag (which is what we use in the lib demo, ref: [index.html](https://github.com/ghiscoding/slickgrid-universal/blob/master/examples/vite-demo-vanilla-bundle/index.html#L8-L14))
```html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'nonce-random-string'; require-trusted-types-for 'script'; trusted-types dompurify">
Expand Down
5 changes: 1 addition & 4 deletions docs/getting-started/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ The easiest is to simply clone the [Aurelia-Slickgrid-Demos](https://github.com/
### 1. Install NPM Package
Install the `Aurelia-Slickgrid`, `Bootstrap` and potentially a font library like `Font-Awesome`
```bash
npm install --save aurelia-slickgrid bootstrap # the last deps are optional

# install required @types
npm install --save-dev @types/sortablejs @types/dompurify
npm install --save aurelia-slickgrid bootstrap # Bootstrap is optional
```
_Note: `Bootstrap` is optional, you can use any other lib that you want_

Expand Down
2 changes: 1 addition & 1 deletion packages/aurelia-slickgrid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@
"@slickgrid-universal/row-detail-view-plugin": "~5.0.0-beta.1",
"@slickgrid-universal/utils": "~5.0.0-beta.1",
"dequal": "^2.0.3",
"isomorphic-dompurify": "^2.8.0",
"sortablejs": "^1.15.2",
"vanilla-calendar-picker": "^2.11.4"
},
"devDependencies": {
"@types/dompurify": "^3.0.5",
"copyfiles": "^2.4.1",
"dompurify": "^3.1.2",
"rimraf": "^5.0.5",
"tslib": "^2.6.2",
"typescript": "^5.4.5"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ const gridStub = {
getSelectionModel: jest.fn(),
registerPlugin: jest.fn(),
setSelectionModel: jest.fn(),
sanitizeHtmlString: (s: string) => s,
onColumnsReordered: new SlickEvent(),
onSelectedRowsChanged: new SlickEvent(),
onSort: new SlickEvent(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import { EventPubSubService } from '@slickgrid-universal/event-pub-sub';
import { SlickRowDetailView as UniversalSlickRowDetailView } from '@slickgrid-universal/row-detail-view-plugin';
import { Constructable, resolve, transient } from 'aurelia';
import DOMPurify from 'isomorphic-dompurify';

import { AureliaViewOutput, GridOption, RowDetailView, ViewModelBindableInputData } from '../models/index';
import { AureliaUtilService } from '../services/aureliaUtil.service';
Expand Down Expand Up @@ -109,11 +108,11 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
// when those are Aurelia View/ViewModel, we need to create View Slot & provide the html containers to the Plugin (preTemplate/postTemplate methods)
if (!this.gridOptions.rowDetailView.preTemplate) {
this._preloadViewModel = this.gridOptions?.rowDetailView?.preloadViewModel;
this.addonOptions.preTemplate = () => DOMPurify.sanitize(`<div class="${PRELOAD_CONTAINER_PREFIX}"></div>`);
this.addonOptions.preTemplate = () => this._grid.sanitizeHtmlString(`<div class="${PRELOAD_CONTAINER_PREFIX}"></div>`) as string;
}
if (!this.gridOptions.rowDetailView.postTemplate) {
this._viewModel = this.gridOptions?.rowDetailView?.viewModel;
this.addonOptions.postTemplate = (itemDetail: any) => DOMPurify.sanitize(`<div class="${ROW_DETAIL_CONTAINER_PREFIX}${itemDetail[this.datasetIdPropName]}"></div>`);
this.addonOptions.postTemplate = (itemDetail: any) => this._grid.sanitizeHtmlString(`<div class="${ROW_DETAIL_CONTAINER_PREFIX}${itemDetail[this.datasetIdPropName]}"></div>`) as string;
}

if (this._grid && this.gridOptions) {
Expand Down
2 changes: 1 addition & 1 deletion packages/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
"bootstrap": "^5.3.3",
"i18next": "^23.11.2",
"i18next-fetch-backend": "^5.0.2",
"isomorphic-dompurify": "^2.8.0",
"rxjs": "^7.8.1"
},
"devDependencies": {
Expand All @@ -75,6 +74,7 @@
"copy-webpack-plugin": "^12.0.2",
"copyfiles": "^2.4.1",
"css-loader": "^7.1.1",
"dompurify": "^3.1.2",
"dotenv-webpack": "^8.1.0",
"fetch-jsonp": "^1.3.0",
"html-webpack-plugin": "^5.6.0",
Expand Down
4 changes: 4 additions & 0 deletions packages/demo/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MyApp } from './my-app';
// However, css files imported in other js/ts files are processed by style-loader.
// import shared from './shared.css';
import { AureliaSlickGridConfiguration } from 'aurelia-slickgrid';
import DOMPurify from 'dompurify';
import { I18nConfiguration } from '@aurelia/i18n';
import Fetch from 'i18next-fetch-backend';
import { RouterConfiguration } from '@aurelia/router';
Expand Down Expand Up @@ -53,6 +54,9 @@ Aurelia
.register(AureliaSlickGridConfiguration.customize(config => {
// change any of the default global options
config.options.gridMenu!.iconCssClass = 'mdi mdi-menu';

// we strongly suggest you add DOMPurify as a sanitizer
config.options.sanitizer = (dirtyHtml) => DOMPurify.sanitize(dirtyHtml, { ADD_ATTR: ['level'], RETURN_TRUSTED_TYPE: true });
}))
.register(DecimalValueConverter, StringifyValueConverter, DateFormatValueConverter)
.app(MyApp)
Expand Down

0 comments on commit 5a84ff8

Please sign in to comment.