Repository navigation
Release v1.9.0
v1.9.0
Changelog
UiKit page contract admin, Playwright e2e + screenshots, SEC-004 hardening: empty roles deny, CSRF fail-closed, consent category allowlist, CSP nonce See UPGRADING.
Security hardening after the SEC-004 audit of 2026-10-27. See UPGRADING.
Security
- Admin access fails closed (M1): an empty
security.access_rolesnow denies everyone; it used to grant the
admin (and so site-wide script authority through any tag manager container) to every visitor, anonymous included. - CSRF required (L1): with
web_ui.enabled: truethe container refuses to compile without
framework.csrf_protection/framework.form.csrf_protection(CsrfProtectionPass); without the form CSRF
extension the admin's CSRF-only forms were valid with no token. The tool form uses the explicit token id
nowo_marketing_kit_tool(I6). - No consent bypass for trackers (L2): categories are an allowlist (
required,preferences,analytics,
marketing); built-in trackers cannot userequired(which skips the consent gate) unless
security.allow_required_trackers: true— rejected in YAML and in the admin form, and earlier database rows are
gated asmarketingat render time. - CSP nonce (L4):
nowo_marketing_head|body_start|body_end(profile, nonce)andsecurity.csp_nonce_attribute
addnonce="…"to the built-in<script>tags; custom HTML stays as configured. - Access before lookup (I2): edit / toggle / delete check access before loading the tool (403 for any id instead
of a 404 / 403 oracle). Vary: Cookie(I3) on responses whose tags were gated by the consent cookies.- Hotjar IDs (I4) must be 1–16 digits without a leading zero.
- Profiles (I5): seed / import YAML refuse a posted profile that is not configured; new / the tool form only
accept configured profiles. - Custom HTML (I7) cannot be saved from the admin while
security.allow_custom_htmlis false. - Docs (L3, L5, L6):
docs/SECURITY.mdthreat model (the admin is site-wide script authority; dedicated role,
audit trail), recipeaccess_controlpath vsweb_ui.path_prefix,.github/SECURITY.mdsupported versions.
Fixed
- Admin
{id}routes accept at most 18 digits, so an id beyondPHP_INT_MAXreturns 404 instead of a 500TypeError(SEC-004 fix verification N1). %env()%provider IDs (as in the Flex recipe and the README) no longer fail the compile-time ID check; they are
validated at render time (I1). The Flex recipe no longer sets the removedadmin/layout.html.twiglayout.
Admin pages on the UiKit page contract (REQ-UI-001 / REQ-UI-003), Playwright e2e + README screenshots (REQ-DEMO-013),
native PHPUnit deprecation gate (REQ-SF-005). See UPGRADING.
Added
web_ui.enabled(admin routes answer 404 when false),web_ui.path_prefix(default/admin/marketing),
web_ui.templates.index|form(per-page template overrides) andweb_ui.form_themes(default the UiKit form theme
@NowoUiKit/form/nowo_ui_layout.html.twig) — the REQ-UI-003 configuration vocabulary.Nowo\MarketingKitBundle\Ui\MarketingKitPages: layout / template / form-theme resolution and theListPage/
FormPageview models of the admin pages.docs/TEMPLATES.md(page → scaffold, view-model fields, data blocks) and the UiKit page contract test
(tests/Integration/Contract/PageContractTest.php).- Translation keys
admin.enable,admin.disable,admin.confirm_delete_title,admin.empty_title,
admin.col.actions,admin.form_intro(7 locales). - Demo: Playwright e2e (
demo/symfony8/e2e,make -C demo/symfony8 test-e2e), README screenshots
(make -C demo/symfony8 demo-screenshots, WebP underdocs/images/demo/),make -C demo/symfony8 reset-demo, and
a demo host layout (nowo_ui_kit.layouts.admin) with a masthead (REQ-DEMO-013).
Changed
- Template contract (REQ-UI-003):
admin/index.html.twigextends the UiKit list scaffold and
admin/form.html.twigthe UiKit form scaffold; they render insidenowo_ui_kit.layouts.admin(or
web_ui.layout_template). Header actions, filters, row actions (Edit, Enable / Disable, Delete), the confirm
dialog, the empty state and flashes come from UiKit; the localmk-*styles and the inlineonsubmit/
onchangehandlers are gone (Delete uses the UiKit confirm<dialog>, the profile filter an Apply button). web_ui.layout_templatedefaults tonull(UiKit layout). A 1.x layout that renders only{% block body %}is
wrapped in the deprecated bridgeadmin/_legacy_layout.html.twig.- The seeded FormKit profile
marketing_kitno longer adds Bootstrap classes (form-control,form-select,
mb-2, …) and setsconvention_requires_translation: true(no raw*.help/*.placeholderkeys in the form). - An invalid tool form submission answers
422(it did throughAbstractController::render()with the form object). - Requirements:
nowo-tech/ui-kit-bundle^2.15(was^1.4 || ^2.2),nowo-tech/form-kit-bundle^2.10(was
^2.0). - PHPUnit fails on any deprecation triggered by the package (
failOnDeprecation,ignoreSuppressionOfDeprecations,
self / direct deprecations not ignored), including Symfony's silencedtrigger_deprecation()calls;
symfony/phpunit-bridgedoes not hook PHPUnit >= 10 (REQ-SF-005). make coverage-checkgates at 100% PHP lines, like the CICode Coveragejob (was 99%) (REQ-TEST-003).
Fixed
- Demo: the session is enabled (
framework.session); Seed catalog, Import from YAML, toggle and delete failed with
SessionNotFoundExceptionwhen adding their flash message (found by the new e2e suite).
Deprecated
web_ui.css_frameworkandweb_ui.icon_set: setnowo_ui_kit.css_framework/nowo_ui_kit.icon_setonce for
every bundle. They still seed the UiKit keys when the host leaves them unset.- The 1.x layout bridge
admin/_legacy_layout.html.twigand the Twig globalsnowo_marketing_kit_layout/
nowo_marketing_kit_css_framework(unused by the pages; removed in 2.0).
Removed
admin/base.html.twigandadmin/layout.html.twig(the bundle's own page shell with inline CSS). The former
defaultweb_ui.layout_templatevalue@NowoMarketingKitBundle/admin/layout.html.twigis treated asnull.- Demo
compose.override.yaml, which published the MySQL service port to the host (REQ-DEMO-006).