Skip to content

Commit

Permalink
feat!: make DOMPurify as optional
Browse files Browse the repository at this point in the history
- replace `isomorphic-dompurify` with `dompurify` and make it a dev deps
  • Loading branch information
ghiscoding committed May 7, 2024
1 parent 7739fb6 commit 8bb566e
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 157 deletions.
2 changes: 1 addition & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
"tsConfig": "tsconfig.app.json",
"allowedCommonJsDependencies": [
"@fnando/sparkline",
"dompurify",
"fetch-jsonp",
"isomorphic-dompurify",
"stream"
],
"assets": [
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 @@ -193,7 +193,7 @@ Since version 4.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
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 @@ -423,10 +423,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
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 'angular-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
3 changes: 0 additions & 3 deletions docs/getting-started/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ Install the `Angular-Slickgrid`, and other external packages like `Bootstrap`
(Bootstrap is optional, you can choose other framework if you wish)
```bash
npm install --save angular-slickgrid bootstrap # the last dep is optional

# install required @types
npm install --save-dev @types/sortablejs @types/dompurify
```
#### Important note about `ngx-translate`
#### Now optional
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@
"@slickgrid-universal/row-detail-view-plugin": "~5.0.0-beta.1",
"@slickgrid-universal/rxjs-observable": "~5.0.0-beta.1",
"dequal": "^2.0.3",
"isomorphic-dompurify": "^2.8.0",
"rxjs": "^7.8.1",
"sortablejs": "^1.15.2"
},
Expand Down Expand Up @@ -106,6 +105,7 @@
"custom-event-polyfill": "^1.0.7",
"cypress": "^13.8.1",
"cypress-real-events": "^1.12.0",
"dompurify": "^3.1.2",
"eslint": "^8.57.0",
"fetch-jsonp": "^1.3.0",
"jest": "^29.7.0",
Expand Down
6 changes: 4 additions & 2 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { AppRoutingRoutingModule } from './app-routing.module';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClient, HttpClientModule } from '@angular/common/http';
Expand All @@ -8,8 +7,10 @@ import { NgSelectModule } from '@ng-select/ng-select';
import { TabsModule } from 'ngx-bootstrap/tabs';
import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import DOMPurify from 'dompurify';

import { AppComponent } from './app.component';
import { AppRoutingRoutingModule } from './app-routing.module';
import { CustomTitleFormatterComponent } from './examples/custom-titleFormatter.component';
import { CustomButtonFormatterComponent } from './examples/custom-buttonFormatter.component';
import { CustomFooterComponent, GridHeaderFooterComponent } from './examples/grid-header-footer.component';
Expand Down Expand Up @@ -155,7 +156,8 @@ export function appInitializerFactory(translate: TranslateService, injector: Inj
autoResize: {
container: '#grid-container',
rightPadding: 10
}
},
sanitizer: (dirtyHtml) => DOMPurify.sanitize(dirtyHtml, { ADD_ATTR: ['level'], RETURN_TRUSTED_TYPE: true })
})
],
providers: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { addToArrayWhenNotExists, castObservableToPromise, SlickRowSelectionMode
import { EventPubSubService } from '@slickgrid-universal/event-pub-sub';
import { SlickRowDetailView as UniversalSlickRowDetailView } from '@slickgrid-universal/row-detail-view-plugin';
import { Observable, Subject } from 'rxjs';
import DOMPurify from 'isomorphic-dompurify';

import { GridOption, RowDetailView } from '../models/index';
import { AngularUtilService } from '../services/angularUtil.service';
Expand Down Expand Up @@ -107,11 +106,11 @@ export class SlickRowDetailView extends UniversalSlickRowDetailView {
// when those are Angular View/ViewModel, we need to create View Component & provide the html containers to the Plugin (preTemplate/postTemplate methods)
if (!this.gridOptions.rowDetailView.preTemplate) {
this._preloadComponent = this.gridOptions?.rowDetailView?.preloadComponent;
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._viewComponent = this.gridOptions?.rowDetailView?.viewComponent;
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;
}

// this also requires the Row Selection Model to be registered as well
Expand Down
Loading

0 comments on commit 8bb566e

Please sign in to comment.