Skip to content

Release v1.9.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 11:50
· 15 commits to main since this release

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_roles now 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: true the 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 use required (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 as marketing at render time.
  • CSP nonce (L4): nowo_marketing_head|body_start|body_end(profile, nonce) and security.csp_nonce_attribute
    add nonce="…" 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_html is false.
  • Docs (L3, L5, L6): docs/SECURITY.md threat model (the admin is site-wide script authority; dedicated role,
    audit trail), recipe access_control path vs web_ui.path_prefix, .github/SECURITY.md supported versions.

Fixed

  • Admin {id} routes accept at most 18 digits, so an id beyond PHP_INT_MAX returns 404 instead of a 500 TypeError (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 removed admin/layout.html.twig layout.

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) and web_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 the ListPage /
    FormPage view 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 under docs/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.twig extends the UiKit list scaffold and
    admin/form.html.twig the UiKit form scaffold; they render inside nowo_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 local mk-* styles and the inline onsubmit /
    onchange handlers are gone (Delete uses the UiKit confirm <dialog>, the profile filter an Apply button).
  • web_ui.layout_template defaults to null (UiKit layout). A 1.x layout that renders only {% block body %} is
    wrapped in the deprecated bridge admin/_legacy_layout.html.twig.
  • The seeded FormKit profile marketing_kit no longer adds Bootstrap classes (form-control, form-select,
    mb-2, …) and sets convention_requires_translation: true (no raw *.help / *.placeholder keys in the form).
  • An invalid tool form submission answers 422 (it did through AbstractController::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 silenced trigger_deprecation() calls;
    symfony/phpunit-bridge does not hook PHPUnit >= 10 (REQ-SF-005).
  • make coverage-check gates at 100% PHP lines, like the CI Code Coverage job (was 99%) (REQ-TEST-003).

Fixed

  • Demo: the session is enabled (framework.session); Seed catalog, Import from YAML, toggle and delete failed with
    SessionNotFoundException when adding their flash message (found by the new e2e suite).

Deprecated

  • web_ui.css_framework and web_ui.icon_set: set nowo_ui_kit.css_framework / nowo_ui_kit.icon_set once for
    every bundle. They still seed the UiKit keys when the host leaves them unset.
  • The 1.x layout bridge admin/_legacy_layout.html.twig and the Twig globals nowo_marketing_kit_layout /
    nowo_marketing_kit_css_framework (unused by the pages; removed in 2.0).

Removed

  • admin/base.html.twig and admin/layout.html.twig (the bundle's own page shell with inline CSS). The former
    default web_ui.layout_template value @NowoMarketingKitBundle/admin/layout.html.twig is treated as null.
  • Demo compose.override.yaml, which published the MySQL service port to the host (REQ-DEMO-006).