Skip to content

feat: add ElementStyles.styleNonce for strict Content Security Policy#7653

Open
AKnassa wants to merge 2 commits into
microsoft:mainfrom
AKnassa:feat/5444-element-styles-nonce
Open

feat: add ElementStyles.styleNonce for strict Content Security Policy#7653
AKnassa wants to merge 2 commits into
microsoft:mainfrom
AKnassa:feat/5444-element-styles-nonce

Conversation

@AKnassa

@AKnassa AKnassa commented Jul 13, 2026

Copy link
Copy Markdown

Pull Request

📖 Description

Under a strict Content Security Policy style-src that does not include 'unsafe-inline', the browser blocks the <style> elements FAST creates, so components rendered through a style-element strategy come out unstyled. The audience that runs strict CSP — enterprise and government — is exactly FAST's audience.

This feature lets an application tell FAST which CSP nonce to stamp on those style elements:

import { FASTElement } from "@microsoft/fast-element";
FASTElement.styleNonce = "{{ csp_nonce }}";
  • ElementStyles.styleNonce — set once at startup; every style element FAST creates carries the nonce. Mirrored as FASTElement.styleNonce for convenience.
  • The nonce is read lazily, at the moment styles are applied — css tagged templates are evaluated at module scope, before application code has a chance to set the nonce, so capturing it at construction time would always observe undefined.
  • Both code paths that create style elements are covered (StyleElementStrategy in element-controller.ts and the fallback strategy in element-styles.ts).
  • Added a ## Security section to the fast-element README and documented the behavior in the Styles section of DESIGN.md.

Adopted stylesheets — the default on every browser FAST supports — are not subject to style-src and are unaffected. This closes the gap on the style-element path, which is reachable via withStrategy() / setDefaultStrategy() and is the automatic fallback where adopted stylesheets are absent.

🎫 Issues

👩‍💻 Reviewer Notes

  • The lazy read is the load-bearing design decision; styles.pw.spec.ts covers the case that matters most — setting the nonce after an ElementStyles instance already exists.
  • FASTElement.styleNonce is defined with Object.defineProperty as a forwarding accessor (not Object.assign, which would copy the value) so ElementStyles.styleNonce stays the single source of truth.
  • Whether StyleElementStrategy should also be exported publicly so applications can opt into it deliberately is deliberately not bundled into this PR — feedback welcome.

📑 Test Plan

New tests in packages/fast-element/src/styles/styles.pw.spec.ts (13 cases) cover: no nonce configured, nonce set after ElementStyles construction, css tagged templates, multiple style elements, re-reading the nonce on every application, resetting to null, removal of nonced style elements, application to a FASTElement shadow root, and the FASTElement.styleNonce mirror. All 13 pass locally in Chromium.

Visual verification — before (styles blocked under strict CSP), after (nonce applied), and control (adopted stylesheets unaffected):

01-before-bug 02-after-fixed 03-control-adopted-stylesheets

✅ Checklist

General

  • I have included a change request file using $ npm run change
  • I have added tests for my changes.
  • I have tested my changes.
  • I have updated the project documentation to reflect my changes.
  • I have read the CONTRIBUTING documentation and followed the standards for this project.

Agents

  • I have linked to an existing issue in this project that this change addresses
  • I have read the skills
  • I have read the DESIGN.md file(s) in packages relevent to my changes
  • I have updated the DESIGN.md file(s) in packages relevent to my changes

⏭ Next Steps

  • element.innerHTML = styles[i] in the style-element path bypasses DOM.policy.createHTML, making it a Trusted Types sink. Out of scope for this change, but worth its own issue.
  • Decide whether StyleElementStrategy should be exported publicly so applications can opt into it deliberately.

Injected <style> elements carried no nonce, so under a strict style-src
(no 'unsafe-inline') the browser blocked them. Applications can now set
`ElementStyles.styleNonce` (mirrored on FASTElement) and it is stamped onto
every style element FAST creates.

The nonce is read lazily at style-application time, because `css` templates
build their ElementStyles at module evaluation, long before application code runs.

Fixes microsoft#5444
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@AKnassa
AKnassa marked this pull request as ready for review July 14, 2026 02:25
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@janechu janechu closed this Jul 16, 2026
@janechu janechu reopened this Jul 16, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@janechu janechu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update your PR based on the skills in this project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add CSP support to fast-element for ElementStyles

2 participants