Skip to content

feat(security): add CSP_STYLE_SRC_STRICT to drop 'unsafe-inline' from style-src#1774

Merged
davidpoblador merged 1 commit into
mainfrom
feat/csp-style-src-strict
May 10, 2026
Merged

feat(security): add CSP_STYLE_SRC_STRICT to drop 'unsafe-inline' from style-src#1774
davidpoblador merged 1 commit into
mainfrom
feat/csp-style-src-strict

Conversation

@davidpoblador

Copy link
Copy Markdown
Member

Summary

  • Adds a new CSP_STYLE_SRC_STRICT env var (default false) on SecurityHeadersSettings. When enabled, style-src switches from 'self' 'unsafe-inline' to 'self' 'nonce-<request-nonce>'.
  • htmx 4.0.0-beta3 already ships its indicator CSS via a constructable CSSStyleSheet and switched the pantry element from inline style to hidden — there are no remaining htmx-side reasons to keep 'unsafe-inline' on style-src.
  • The framework's own templates already nonce every <style> tag (skeleton.html.jinja:28, theme.html.jinja:14), so a strict style-src works out of the box for the framework. The flag is opt-in because user templates may still rely on inline style="..." attributes which are blocked under nonce-only style-src.
  • Documents the new flag and the migration steps in development-guide.md.

Why opt-in (not flip the default)?

Removing 'unsafe-inline' is a meaningful breaking change for any user template that uses inline style="..." attributes (those are blocked under nonce-only style-src — nonces don't cover style attributes). Shipping this as opt-in lets CSP-conscious users harden their setup today; the default may flip in a future major version once the ecosystem migrates.

Test plan

  • uv run pytest tests/unit/test_security_headers_middleware.py — 28 passed (3 new tests for strict mode)
  • uv run pytest tests/unit/ — 796 passed
  • Pre-commit hooks pass (ruff, rumdl, etc.)
  • Manually flip CSP_STYLE_SRC_STRICT=true in a scaffolded project and verify no CSP violations on the default pages

🤖 Generated with Claude Code

… style-src

htmx 4.0.0-beta3 ships indicator CSS via a constructable CSSStyleSheet
and the pantry element via the hidden attribute, removing the last
htmx-side reasons to allow 'unsafe-inline' on style-src. The framework's
own templates already nonce every <style> tag, so a strict style-src
needs no further framework changes.

Add a CSP_STYLE_SRC_STRICT flag (default: false) on SecurityHeadersSettings.
When enabled, style-src becomes 'self' 'nonce-<request-nonce>' instead of
'self' 'unsafe-inline'. Default stays permissive to avoid breaking
projects that rely on inline style="..." attributes.

The default may flip in a future major version once the ecosystem
migrates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@davidpoblador davidpoblador merged commit 3f74886 into main May 10, 2026
2 checks passed
@davidpoblador davidpoblador deleted the feat/csp-style-src-strict branch May 10, 2026 08:55
davidpoblador added a commit that referenced this pull request May 10, 2026
## Summary

Trivial reorder of two adjacent CSP subsections in
`development-guide.md`. The auto-merge of #1774 placed *Strict
style-src* before *htmx Nonce Protection*; the htmx-specific section
reads more naturally first since this project's frontend story leads
with htmx.

No content changes — just swapping section order.

## Test plan

- [x] Pre-commit hooks pass (rumdl)
- [ ] Spot-check rendered docs site

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
davidpoblador added a commit that referenced this pull request May 10, 2026
#1776)

## Summary

Catches up the LLM-targeted docs (`llms.txt`, `llms-full.txt`) and the
scaffolded `vibetuner-template/.claude/rules/frontend.md` with the htmx
beta3 features that landed in #1771, #1773, and #1774. The project's
`CLAUDE.md` requires every feature PR to update those alongside the docs
site, and I missed them in the original three.

- **`llms.txt`** — extends the CSP bullet with `CSP_STYLE_SRC_STRICT`
and adds an `hx-nonce` opt-in bullet
- **`llms-full.txt`** — adds strict style-src + hx-nonce paragraphs to
the Security Headers section, and a "Beta3 Additions" subsection under
the HTMX migration coverage
- **`vibetuner-template/.claude/rules/frontend.md`** — drops the stale
"`hx-on::` shorthand is broken in alpha8" claim (works in beta1+)

## Test plan

- [x] `rumdl` lint passes (pre-commit)
- [x] No new long-line violations in edited regions
- [ ] Spot-check rendered docs site

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
davidpoblador added a commit that referenced this pull request May 11, 2026
…1779)

Closes #1778.

## Summary

Both htmx-nonce and `CSP_STYLE_SRC_STRICT` were documented in the same
wave that introduced them (#1773, #1774, #1776), but they don't yet work
on a freshly scaffolded project today because the released packages
predate those PRs:

- `@alltuner/vibetuner@10.10.0` on npm pulls `htmx.org@4.0.0-beta2`,
which doesn't ship `dist/ext/hx-nonce.js`. The bundler fails with `Could
not resolve "./node_modules/htmx.org/dist/ext/hx-nonce.js"`.
- `vibetuner==10.10.0` on PyPI predates the `style_src_strict` settings
field. Pydantic's `extra="ignore"` swallows `CSP_STYLE_SRC_STRICT`
silently; the CSP header keeps emitting `'unsafe-inline'`.

Both self-resolve as soon as release-please cuts 10.11.0. Until then,
point readers at the version requirement so they don't burn time
debugging.

This adds:

- An admonition note above each opt-in subsection in
`development-guide.md` calling out the minimum version and the failure
mode users will see.
- The same min-version constraint inline in `llms-full.txt` and
`llms.txt`, since those are LLM-targeted and need to be self-contained.

No framework code changes. Confirmed during the post-merge smoke test
(issue #1778) that with the framework installed editable from `main`,
both opt-ins work end-to-end: strict CSP becomes `style-src 'self'
'nonce-…'`, the bundle build succeeds, and
`htmx:security:strip`/`htmx:security:violation` identifiers all appear
in `bundle.js`.

## Test plan

- [x] `rumdl` lint passes (pre-commit)
- [x] No new long-line violations in edited regions
- [ ] Spot-check rendered docs site (admonitions look reasonable)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
davidpoblador pushed a commit that referenced this pull request May 11, 2026
🤖 I have created a release *beep* *boop*
---


##
[10.11.0](v10.10.0...v10.11.0)
(2026-05-11)


### Features

* **htmx:** prepare framework templates for hx-nonce extension
([#1773](#1773))
([16137e8](16137e8))
* **security:** add CSP_STYLE_SRC_STRICT to drop 'unsafe-inline' from
style-src ([#1774](#1774))
([3f74886](3f74886))


### Bug Fixes

* **deps:** update dependency htmx.org to v4.0.0-beta3
([#1770](#1770))
([d21d883](d21d883))


### Documentation Updates

* gate htmx-nonce + strict style-src behind a min-version note
([#1779](#1779))
([bc6d365](bc6d365))
* **htmx:** document beta3 features and correct migration guide
([#1771](#1771))
([2921dbc](2921dbc))
* put htmx Nonce Protection before Strict style-src
([#1775](#1775))
([d1254c9](d1254c9))
* sync llms.txt, llms-full.txt, and frontend rules with htmx beta3
([#1776](#1776))
([b486470](b486470))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

1 participant