Repository navigation
fix: apply DOM sink guards consistently across entrypoints - #7772
Merged
Merged
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 25176605-2de9-4537-b426-11537e2d8fd1
Jane Chu (janechu)
requested review from
Chris Holt (chrisdholt) and
John Kreitlow (radium-v)
as code owners
October 20, 2026 22:54
Chris Holt (chrisdholt)
previously approved these changes
Oct 20, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 25176605-2de9-4537-b426-11537e2d8fd1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b7e52b66-3773-4522-9275-d44cbf9f8abd
Chris Holt (chrisdholt)
previously approved these changes
Oct 26, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b7e52b66-3773-4522-9275-d44cbf9f8abd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
📖 Description
Use the existing DOM sink guards as FAST's internal default so ESM root,
development, focused, declarative, and bundled entrypoints consistently protect
binding sinks without application setup.
Reuse the existing Trusted Types policy and remove redundant bundle policy
installation, preserving the application's one-time
DOM.setPolicy()override.Public API signatures and explicit template/binding policy precedence are
unchanged.
Keep baseline guard execution separate from custom-policy configuration, and
route aspect-only directive imports through the dependency-neutral module.
Focused imports retain only the policy code they need, without duplicating or
weakening the existing guards.
Use compact, aspect-aware element rules and direct global guard selection.
Custom policies snapshot only supplied overrides and resolve missing guards
through the same built-in selectors, preserving null removals, undefined
fallback, property casing, and element-before-global precedence.
Coerce non-nullish guarded URL values once before validation and pass the
validated string to the sink. This closes an existing bypass where arrays or
objects skipped string-only checks and were later coerced by the DOM. Preserve
null/undefined behavior and application policy overrides.
Include migration guidance, updated export-size reports, a patch change file,
and cross-browser regressions. The declarative rich-HTML fixture now explicitly
allows only its known static markup and checks fresh client rendering as well
as server-rendered markup.
🎫 Issues
No existing issue is linked.
👩💻 Reviewer Notes
This intentionally tightens behavior for previously unconfigured ESM consumers:
guarded URL bindings reject unsafe protocols, and known dangerous sinks such as
:innerHTMLand inline handler bindings require an explicit appropriate policy.Normal text bindings and
@clicklisteners remain supported. The baseline isnot an HTML sanitizer and does not make untrusted template source safe.
Check that applications can still install a custom global policy, including
when using the bundles. Entry-point regressions run under a CSP that disallows
duplicate
fast-htmlpolicy creation.The dependency optimization removes custom-policy configuration code from core imports
and prevents standalone directives from initializing DOM policies. Regenerated
package reports show these gzip sizes:
FASTElementhtmlrefchildrenslottedrepeatCompared with
c9ff0ed51, compact dispatch saves 341–342 minified bytes,14–41 gzip bytes, and 21–32 Brotli bytes in each affected focused export.
The CDN bundle saves 502 minified bytes, 47 gzip bytes, and 13 Brotli bytes.
All other measured exports are unchanged; no measured minified, gzip, or
Brotli size increases. These are modest savings, not removal of baseline
protection.
Core and template imports still include baseline protection, so their remaining
increase over main is intentional. All original guard rules and URL-validation
behavior are preserved.
Before the URL-coercion follow-up, local Chromium comparison against the main-branch implementation, using medians
of nine alternating samples with detached DOM and synchronous binding updates:
These are synthetic local measurements, not application-level guarantees.
URL validation adds about 0.19 microseconds per write in this workload; the
compile/render samples were noisy. Ordinary sinks remain unwrapped, and guard
selection is cached when binding behaviors are created.
📑 Test Plan
New regressions cover eight representative entrypoints, URL attributes and properties,
blocked HTML/inline-handler sinks, normal text and events, restrictive Trusted
Types CSP, custom global policies, and rejection of a second override. The
rich-HTML fixture also asserts fresh client rendering and no page errors.
Five additional dependency regressions verify that
FASTElementandhtmldonot import the custom-policy builder and that
ref,children, andslotteddo not import DOM policy initialization.
Three URL-coercion regressions cover both default and custom policies, attribute
and property sinks, unsafe arrays/boxed strings/objects, safe coercible values,
nullish behavior, a single stateful coercion, and initial/later binding updates.
All three failed on the previous PR head before the fix.
Three compact-dispatch regressions cover the complete element guard matrix,
case-sensitive property distinctions, guard removals and global fallback,
undefined overrides, and configuration snapshots at every map level. A local
comparison against the previous head also matched 1,067,430 default/custom
sink/value combinations, including every blocked event name and all six aspects.
npm run buildnpm run testnpm exec -w @microsoft/fast-element -- playwright test src/dom-policy.pw.spec.ts --project=chromium --retries=0npm exec -w @microsoft/fast-element -- playwright test src/dom-policy.pw.spec.ts --project=chromium --grep 'focused entrypoint policy dependencies' --retries=0npm exec -w @microsoft/fast-element -- playwright test src/dom-policy.pw.spec.ts --project=chromium --grep 'validates coerced URL' --retries=0npx playwright test --config packages/fast-element/playwright.config.ts packages/fast-element/src/dom-policy.pw.spec.ts --retries=0 --reporter=linenpm run build:tsc -w @microsoft/fast-element && npm run build:rollup -w @microsoft/fast-element && npm run build:sizes -w @microsoft/fast-elementnpm exec -w @microsoft/fast-element -- playwright test --config=playwright.declarative.config.ts test/declarative/fixtures/bindings/content/content.spec.ts --retries=0npm run biome:checknpm run format:checknpm run checkchange✅ Checklist
General
$ npm run changeAgents
⏭ Next Steps
None required.