Skip to content

Commit 074d96b

Browse files
authored
[flags] land enableTrustedTypesIntegration (facebook#35816)
## Summary This flag enables React's integration with the browser [Trusted Types API](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API). The Trusted Types API is a browser security feature that helps prevent DOM-based XSS attacks. When a site enables Trusted Types enforcement via `Content-Security-Policy: require-trusted-types-for 'script'`, the browser requires that values passed to DOM injection sinks (like `innerHTML`) are typed objects (`TrustedHTML`, `TrustedScript`, `TrustedScriptURL`) created through developer-defined sanitization policies, rather than raw strings. ### What changed Previously, React always coerced values to strings (via `'' + value`) before passing them to DOM APIs like `setAttribute` and `innerHTML`. This broke Trusted Types because it converted typed objects into plain strings, which the browser would then reject under Trusted Types enforcement. React now passes values directly to DOM APIs without string coercion, preserving Trusted Types objects so the browser can validate them. This applies to `dangerouslySetInnerHTML`, all HTML and SVG attributes, and URL attributes (`href`, `action`, etc). ### Before (broken) Using Trusted Types with something like`dangerouslySetInnerHTML` would throw: ```js const sanitizer = trustedTypes.createPolicy('sanitizer', { createHTML: (input) => DOMPurify.sanitize(input), }); function Comment({text}) { const clean = sanitizer.createHTML(text); // clean is a TrustedHTML object, but React would call '' + clean, // converting it back to a plain string before setting innerHTML. // Under Trusted Types enforcement, the browser rejects the string: // // TypeError: Failed to set 'innerHTML' on 'Element': // This document requires 'TrustedHTML' assignment. return <div dangerouslySetInnerHTML={{__html: clean}} />; } ``` ### After (works) React now passes the TrustedHTML object directly to the DOM without stringifying it: ```js const policy = trustedTypes.createPolicy('sanitizer', { createHTML: (input) => DOMPurify.sanitize(input), }); function Comment({text}) { // TrustedHTML objects are passed directly to innerHTML return <div dangerouslySetInnerHTML={{__html: policy.createHTML(text)}} />; } function UserProfile({bio}) { // String attribute values also preserve Trusted Types objects return <div data-bio={policy.createHTML(bio)} />; } ``` ## Non-breaking change - Sites using Trusted Types: React no longer breaks Trusted Types enforcement. TrustedHTML and TrustedScriptURL objects passed through React props are forwarded to the DOM without being stringified. - Sites not using Trusted Types: No behavior change. DOM APIs accept both strings and Trusted Types objects, so removing the explicit string coercion is functionally identical.
1 parent e33071c commit 074d96b

8 files changed

Lines changed: 7 additions & 9 deletions

packages/shared/ReactFeatureFlags.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ export const disableLegacyMode: boolean = true;
208208
// in open source, but www codebase still relies on it. Need to remove.
209209
export const disableCommentsAsDOMContainers: boolean = true;
210210

211-
export const enableTrustedTypesIntegration: boolean = false;
211+
export const enableTrustedTypesIntegration: boolean = true;
212212

213213
// Prevent the value and checked attributes from syncing with their related
214214
// DOM properties

packages/shared/forks/ReactFeatureFlags.native-fb.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const enableSuspenseAvoidThisFallback: boolean = false;
6262
export const enableSuspenseCallback: boolean = true;
6363
export const enableTaint: boolean = true;
6464
export const enableTransitionTracing: boolean = false;
65-
export const enableTrustedTypesIntegration: boolean = false;
65+
export const enableTrustedTypesIntegration: boolean = true;
6666
export const enableUpdaterTracking: boolean = __PROFILE__;
6767
export const retryLaneExpirationMs = 5000;
6868
export const syncLaneExpirationMs = 250;

packages/shared/forks/ReactFeatureFlags.native-oss.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export const enableSuspenseAvoidThisFallback: boolean = false;
5050
export const enableSuspenseCallback: boolean = false;
5151
export const enableTaint: boolean = true;
5252
export const enableTransitionTracing: boolean = false;
53-
export const enableTrustedTypesIntegration: boolean = false;
53+
export const enableTrustedTypesIntegration: boolean = true;
5454
export const passChildrenWhenCloningPersistedNodes: boolean = false;
5555
export const retryLaneExpirationMs = 5000;
5656
export const syncLaneExpirationMs = 250;

packages/shared/forks/ReactFeatureFlags.test-renderer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const disableInputAttributeSyncing: boolean = false;
2626
export const enableScopeAPI: boolean = false;
2727
export const enableCreateEventHandleAPI: boolean = false;
2828
export const enableSuspenseCallback: boolean = false;
29-
export const enableTrustedTypesIntegration: boolean = false;
29+
export const enableTrustedTypesIntegration: boolean = true;
3030
export const disableTextareaChildren: boolean = false;
3131
export const enableSuspenseAvoidThisFallback: boolean = false;
3232
export const enableCPUSuspense: boolean = false;

packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const enableSuspenseAvoidThisFallback = false;
4747
export const enableSuspenseCallback = false;
4848
export const enableTaint = true;
4949
export const enableTransitionTracing = false;
50-
export const enableTrustedTypesIntegration = false;
50+
export const enableTrustedTypesIntegration = true;
5151
export const enableUpdaterTracking = false;
5252
export const passChildrenWhenCloningPersistedNodes = false;
5353
export const retryLaneExpirationMs = 5000;

packages/shared/forks/ReactFeatureFlags.test-renderer.www.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const enableCreateEventHandleAPI: boolean = false;
2828
export const enableSuspenseCallback: boolean = true;
2929
export const disableLegacyContext: boolean = false;
3030
export const disableLegacyContextForFunctionComponents: boolean = false;
31-
export const enableTrustedTypesIntegration: boolean = false;
31+
export const enableTrustedTypesIntegration: boolean = true;
3232
export const disableTextareaChildren: boolean = false;
3333
export const enableSuspenseAvoidThisFallback: boolean = true;
3434
export const enableCPUSuspense: boolean = false;

packages/shared/forks/ReactFeatureFlags.www-dynamic.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ export const enableFragmentRefs: boolean = __VARIANT__;
3636
export const enableFragmentRefsScrollIntoView: boolean = __VARIANT__;
3737
export const enableFragmentRefsTextNodes: boolean = __VARIANT__;
3838
export const enableInternalInstanceMap: boolean = __VARIANT__;
39-
export const enableTrustedTypesIntegration: boolean = __VARIANT__;
4039
export const enableParallelTransitions: boolean = __VARIANT__;
4140

4241
export const enableEffectEventMutationPhase: boolean = __VARIANT__;

packages/shared/forks/ReactFeatureFlags.www.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export const {
2525
enableObjectFiber,
2626
enableRetryLaneExpiration,
2727
enableTransitionTracing,
28-
enableTrustedTypesIntegration,
2928
retryLaneExpirationMs,
3029
syncLaneExpirationMs,
3130
transitionLaneExpirationMs,
@@ -45,7 +44,7 @@ export const enableProfilerTimer = __PROFILE__;
4544
export const enableProfilerCommitHooks = __PROFILE__;
4645
export const enableProfilerNestedUpdatePhase = __PROFILE__;
4746
export const enableUpdaterTracking = __PROFILE__;
48-
47+
export const enableTrustedTypesIntegration: boolean = true;
4948
export const enableSuspenseAvoidThisFallback: boolean = true;
5049

5150
export const enableAsyncDebugInfo: boolean = true;

0 commit comments

Comments
 (0)