basecamp/trix · error · TypeError

TRUSTED_TYPES_POLICY configuration option must provide a "cr

Error message

TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.

What it means

Companion check to the createHTML requirement: when a TRUSTED_TYPES_POLICY is supplied in the config, DOMPurify also requires a createScriptURL function. The policy is used to sign both HTML output and script URLs (e.g. for allowed src attributes), so a missing createScriptURL would leave script URL sinks unsigned or unsafe. DOMPurify throws this TypeError at configuration time, fail-closed.

Source

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

      }
      /* 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) {
        // Explicit opt-out for this call: perform no Trusted Types signing and
        // create nothing (so a strict `trusted-types` CSP that disallows a
        // `dompurify` policy can still call `sanitize` from inside its own

View on GitHub (pinned to 4700401311)

Solutions

  1. Add a createScriptURL function to the policy object (an identity or allow-list-based signer is fine).
  2. Verify the key spelling is exactly createScriptURL.
  3. If you never load scripts from sanitized content, still supply a strict createScriptURL that throws or returns only known-safe URLs.
  4. Prefer trustedTypes.createPolicy(name, {createHTML, createScriptURL}) so both methods always exist.

Example fix

// before
DOMPurify.sanitize(dirty, { TRUSTED_TYPES_POLICY: { createHTML: (s) => s } });
// after
DOMPurify.sanitize(dirty, {
  TRUSTED_TYPES_POLICY: {
    createHTML: (s) => s,
    createScriptURL: (url) => ALLOWED_URLS.includes(url) ? url : 'about:blank'
  }
});
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

const hasCreateScriptURL = (p) => !!p && typeof p.createScriptURL === 'function';

Try / catch

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

Prevention

When it happens

Trigger: DOMPurify.sanitize(dirty, { TRUSTED_TYPES_POLICY: { createHTML: fn } }) — a policy object providing only createHTML, or a natively created policy object whose createScriptURL is absent or not a function.

Common situations: Copy-pasting a minimal policy example that only handles HTML; building a policy for a browser where you thought script URLs were irrelevant but DOMPurify still validates both; property name typos (createScriptUrl).

Related errors


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