basecamp/trix · error · TypeError

TRUSTED_TYPES_POLICY configuration option must provide a "cr

Error message

TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.

What it means

DOMPurify validates a caller-supplied TRUSTED_TYPES_POLICY at _parseConfig time: the object must expose both a createHTML and a createScriptURL function. If TRUSTED_TYPES_POLICY is provided but createHTML is missing or not a function, DOMPurify throws this TypeError rather than silently producing unsigned/unsafe output. This fail-closed check also prevents a stale foreign policy from signing later 'default' results (see GHSA-vxr8-fq34-vvx9).

Source

Thrown at action_text-trix/app/assets/javascripts/trix.js:2651

      /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
      if (WHOLE_DOCUMENT) {
        addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
      }
      /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
      if (ALLOWED_TAGS.table) {
        addToSet(ALLOWED_TAGS, ['tbody']);
        delete FORBID_TAGS.tbody;
      }
      // Re-derive the active Trusted Types policy from this configuration on
      // every parse. The active policy must never be sticky closure state that
      // outlives the config that set it: a caller-supplied policy left in place
      // after `clearConfig()` — or after a later call that supplied none, or
      // `TRUSTED_TYPES_POLICY: null` — could sign a subsequent "default"
      // `RETURN_TRUSTED_TYPE` result with a foreign, possibly unsafe policy.
      // See GHSA-vxr8-fq34-vvx9.
      if (cfg.TRUSTED_TYPES_POLICY) {
        if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
          throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
        }
        if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
          throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
        }
        // A caller-supplied policy applies to this configuration only.
        const previousTrustedTypesPolicy = trustedTypesPolicy;
        trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
        // Sign local variables required by `sanitize`. If the supplied policy's
        // `createHTML` is circular (i.e. it calls `DOMPurify.sanitize`), this
        // throws via the re-entrancy guard. Restore the previous policy first so
        // the instance is not left in a poisoned state. See #1422.
        try {
          emptyHTML = _createTrustedHTML('');
        } catch (error) {
          trustedTypesPolicy = previousTrustedTypesPolicy;
          throw error;
        }
      } else if (cfg.TRUSTED_TYPES_POLICY === null) {

View on GitHub (pinned to 4700401311)

Solutions

  1. Add a createHTML function to the object passed as TRUSTED_TYPES_POLICY.
  2. Check spelling/casing: the key must be exactly createHTML (and createScriptURL), not createHtml.
  3. If you only need one direction, still provide both methods (unused ones can be identity functions).
  4. Use trustedTypes.createPolicy(...) to build the object, then verify both methods exist before passing it.

Example fix

// before
DOMPurify.sanitize(dirty, { TRUSTED_TYPES_POLICY: { createScriptURL: (s) => s } });
// after
DOMPurify.sanitize(dirty, {
  TRUSTED_TYPES_POLICY: {
    createHTML: (s) => s,
    createScriptURL: (s) => s
  }
});
Defensive patterns

Strategy: type-guard

Validate before calling

if (cfg.TRUSTED_TYPES_POLICY && typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
  throw new Error('Refusing to configure: TRUSTED_TYPES_POLICY.createHTML is missing');
}

Type guard

const hasCreateHTML = (p) => !!p && typeof p.createHTML === 'function';

Try / catch

try {
  DOMPurify.sanitize(dirty, { TRUSTED_TYPES_POLICY: policy });
} catch (e) {
  if (String(e.message).includes('createHTML')) {
    console.error('Supplied policy lacks createHTML; fix policy object.');
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling DOMPurify.addHook-free configuration such as DOMPurify.sanitize(dirty, { TRUSTED_TYPES_POLICY: someObject }) where someObject has no createHTML function (e.g. only createScriptURL, or a TrustedTypes policy object that is null/partially constructed), or after clearConfig() followed by a config supplying only one hook.

Common situations: Hand-rolling a policy object and forgetting one of the two required methods; passing a native TrustedTypes policy created with only createHTML; typos in property names (createHtml vs createHTML); passing the wrong variable (a config object instead of a policy).

Related errors


AI-assisted analysis of basecamp/trix@4700401311 (2026-09-02). Data as JSON: /api/errors/bc32767aa82bb2a9. Report an issue: GitHub.