Repository navigation
Release v2.16.0
v2.16.0
Changelog
FormKit: SEC-004 BH hardening — help-modal nonce (escape-by-default, frame-only reuse with private+Vary), CSRF factory allowlist, stateless_csrf form_types validation See UPGRADING.
Security
AI security audit (REQ-SEC-004) of v2.15.0 on 2026-10-30: Pass (good) / Low; all findings fixed here. See UPGRADING — two changes need host action when you use help_modal.
- Help modal payloads are bound to the page (L1).
help-modal.jshonoured anylabel[data-nowo-help-modal]inside a<form>, so sanitised user HTML that keepsform/label/data-*(e.g. DOMPurify defaults) rendered in a page could injecticon_html/title_html/contentintoinnerHTML(stored XSS). Every payload now carries a per-request nonce (HelpModalNonce, kept in the main request attributes, worker-safe) and the script ignores payloads whose nonce differs from<meta name="nowo-form-kit-help-modal-nonce">in<head>, printed by the new Twig functionnowo_form_kit_help_modal_meta()(nowo_form_kit_help_modal_nonce()returns the bare value). Same-origin Turbo fetches send the page nonce inX-Nowo-Form-Kit-Help-Modal-Nonceand the server reuses it for Turbo Frame fragments; Live Component morphs keep the last bound payload of a label. - Help modal nonce header cannot prime shared caches (L3). The page nonce sent back in
X-Nowo-Form-Kit-Help-Modal-Nonceis honoured only on same-origin Turbo Frame requests (Turbo-Frameheader;Sec-Fetch-Site: same-originor a matchingOrigin/Referer), never on top-level pages, and the newHelpModalNonceResponseListenermarks those responsesCache-Control: private+Vary: X-Nowo-Form-Kit-Help-Modal-Nonce. Pages that renderhelp_modalfields together with user content must not be served from a shared cache (documented in SECURITY.md). - Help modal text is escaped by default (L1).
titleandcontentare inserted as text; HTML needs the explicittitle_html(unchanged) or the newcontent_htmloption.icon_htmlstays trusted developer HTML. - Help modal portal only moves its own roots (L2). Elements that merely carry
data-nowo-formkit-help-modalare no longer relocated todocument.body. - Browser logger off by default (I7).
help-modal.jsdiagnostics needwindow.NOWO_FORM_KIT_DEBUG = true; a payload parse error logs the length only, never the raw attribute. - Smaller dist archive (I8). New
.gitattributesexport-ignoresdemo/(demoAPP_SECRET,0.0.0.0port),tests/,specs/,.specify/,.scripts/,.githooks/, the e2e suite, Vitest specs and CI / tooling files. - CSRF-only forms reject safe methods and empty token ids (I9).
CsrfOnlyFormFactory::create()/createNamed()/createWithFields()andcsrf_action_form()throwInvalidArgumentExceptionfor any method other than POST / PUT / PATCH / DELETE (GET, HEAD, OPTIONS, TRACE, empty…) and for an empty or blank CSRF token id. stateless_csrf.form_typesrejects base types (I4).FormTypeand every type underSymfony\Component\Form\/Symfony\Bridge\(e.g.TextType,EntityType) fail configuration: a stateless token id there would apply to every form.%env()%entries are rejected too (they would bypass the check).
Changed
help-modal.jsrebuilt from TypeScript (Vitest 100% statements / branches / functions / lines kept).- Demo:
base.html.twigprintsnowo_form_kit_help_modal_meta(); the contact form help modal usescontent_html.
What's Changed
- fix(security): REQ-SEC-004 2026-10-30 hardening (help modal nonce, CSRF-only forms, dist) by @HecFranco in #54
Full Changelog: v2.15.0...v2.16.0