Skip to content

Security: ascentspark/react-datatables

Security

docs/SECURITY.md

Security

@ascentsparksoftware/react-datatables-net wraps DataTables, which renders cell content by writing innerHTML. This is powerful but means DataTables does not escape cell data by default — unlike JSX, where React escapes text for you. Untrusted data rendered through DataTables is therefore an XSS sink unless you take action. This document explains the protections this library provides and their limits.

1. Escape by default, escapeByDefault

<DataTablesProvider styling={defaultStyling} escapeByDefault>
  <App />
</DataTablesProvider>

escapeByDefault (on DataTablesProvider, overridable per table via the <DataTable> prop of the same name) makes the component append a lowest-priority { targets: '_all', render: escapeHtmlRenderer() } to columnDefs. Because DataTables gives columns[].render (and earlier columnDefs) precedence, only columns without an explicit renderer are escaped — your custom/orthogonal renderers and defaultContent columns are untouched.

escapeHtmlRenderer() escapes only string values for the display/filter render types (& < > " '). Numbers, booleans, null/undefined and objects pass through unchanged so numeric formatting, defaultContent and null-data columns keep working; sort/type values stay raw so ordering and filtering remain correct.

Recommendation: enable escapeByDefault in every app. Treat it as the default posture.

You can also apply escapeHtmlRenderer() to a single column explicitly:

import { escapeHtmlRenderer } from '@ascentsparksoftware/react-datatables-net';

const columns = [{ data: 'comment', render: escapeHtmlRenderer() }];

2. Opt-in HTML, unsafeHtmlRenderer()

When a column genuinely needs to render HTML, opt in explicitly. unsafeHtmlRenderer() passes the given HTML through unchanged for the display type, so it must only ever be fed trusted, already-sanitized markup — hardcoded strings, or the output of a sanitizer such as DOMPurify run by you:

import DOMPurify from 'dompurify';
import { unsafeHtmlRenderer } from '@ascentsparksoftware/react-datatables-net';

const columns = [
  // You sanitize; the renderer only routes the trusted result to 'display'.
  { data: 'bio', render: unsafeHtmlRenderer((row) => DOMPurify.sanitize(row.bioHtml)) },
];

Sort/filter/type still receive the raw underlying data so ordering and search stay correct.

The trust boundary is yours: feeding user-controlled data to unsafeHtmlRenderer() — or writing your own render that returns raw HTML — is an XSS vulnerability. The function name says "unsafe" because that trust decision belongs to you.

3. React cell content is safe

A column's cell render prop renders through React portals, not through DataTables' HTML path:

const columns: DtColumn<User>[] = [
  { data: 'name', title: 'Name', cell: ({ row }) => <a href={`/user/${row.id}`}>{row.name}</a> },
];

React escapes text nodes in JSX, so a cell render prop is safe by construction for untrusted data (unless you reach for dangerouslySetInnerHTML yourself, which reintroduces the sink). For columns that need rich display content, prefer cell over string-building render functions.

4. The editing engine writes through the Api

Edit-in-place controls are real DOM elements (or React components rendered via portals), never innerHTML, and committed values are written through the DataTables Api (cell().data(v) + draw(false)), flowing back through the cell's normal display path — which still honours the escaping renderers above. No edit path introduces an HTML sink. See Edit in place.

5. CSP and Trusted Types, a known limitation

This library itself adds no eval, no Function constructor and no innerHTML — React content mounts via createPortal, editors are built with createElement, and commits go through the DataTables Api. It is compatible with a strict script-src CSP (no 'unsafe-eval' needed on our account).

DataTables, however, assigns innerHTML internally when it renders cells. Under a strict Content-Security-Policy: require-trusted-types-for 'script', innerHTML assignment is a Trusted Types sink, so DataTables is not compatible with strict Trusted Types out of the box. Options:

  • Do not enable require-trusted-types-for 'script' for routes that use DataTables, or
  • Register a permissive Trusted Types policy (e.g. a default policy that passes HTML through) and accept that DataTables-injected HTML is not TT-guarded — which is why escapeByDefault and the renderers above remain essential.

This is a property of DataTables itself, not of the React wrapper. We document it rather than claim Trusted Types support we cannot honestly provide.

6. Supply chain

  • datatables.net (and its transitive jquery), react and react-dom are peer dependencies — the consuming app owns, dedupes and audits them, so security advisories are resolved in your tree, not pinned by this package.
  • The DataTables engine is imported lazily in the browser only; it never enters the server module graph of a Next.js app.
  • The published package has no runtime dependencies.

Reporting

Report suspected vulnerabilities privately to the maintainer (Ascentspark) rather than via public issues.

There aren't any published security advisories