• v0.9.2 653ee43126

    v0.9.2 Stable

    Robin released this 2026-09-28 19:19:54 +02:00 | 14 commits to main since this release

    A security release first, a feature release second. Update as soon as you can — the security section below is honest about what was exposed in 0.9.1 and earlier. Then the good news: popups that behave like any other form, a Newsletters dashboard that actually tells you something, and a list view when the cards get in the way.

    🔒 Security — please read

    We ran a full audit of the plugin (output escaping, every server entry point, the public forms and links, SQL, stored content). Good news: no cross-site scripting and no SQL injection were found. Less good news: several real problems were, and this release closes all of them. In order of seriousness:

    • Your newsletters, email templates and forms were readable by anyone through the WordPress REST API (/wp-json/wp/v2/lrob-etk-…), without logging in — including the content of unsent newsletters and the recipient addresses configured on your contact forms. WordPress does not hide "private" post types from REST; the plugin now does. Gutenberg and the block pickers keep working.
    • The signup form could be turned into an email cannon: no rate limit, and one confirmation email per request to any address you typed. There is now a budget per visitor, one confirmation email per address every ten minutes (five a day at most), and the same rule on address changes from the preferences page.
    • Anyone could subscribe any WordPress account of your site — typing an account's email opted it in on the spot, added it to lists and recorded a "consent granted" entry in its name. Now an account joins either signed in, or through a confirmation email sent to the account itself.
    • The per-visitor rate limit could be bypassed by sending a fake X-Forwarded-For header. The plugin trusts the real connection address unless you tell it which proxy header to trust (LROB_ETK_TRUSTED_PROXY_HEADER in wp-config.php, e.g. for Cloudflare).
    • The built-in captchas (math question, picture) carried their own answer in a hidden field a bot could decode, and never expired. Tokens no longer reveal anything, last fifteen minutes and work once.
    • A forged bounce report mailed to your sending address could unsubscribe or suppress any subscriber. Every email now carries a signed recipient tag; a report that doesn't prove it answers one of your emails counts, at most, as one soft bounce.
    • Link previews could act on someone's behalf: a mail scanner prefetching the "decline" or "confirm my new address" link did the deed. Both land on a page with a button now.
    • The open pixel counted people who had not consented to be measured (only the "deliverability" pixel was allowed for them). Opens follow the same consent rule as clicks, and count at most once per hour.
    • A visitor's uploaded SVG opened inline as your site when an administrator clicked it in the inbox — a scripted SVG would have run with admin rights. Only images and PDFs open inline; everything else downloads, and every file response is sandboxed.
    • A visitor's name could add hidden recipients to the notification's Reply-To (Ann <x@evil>, Bob). Cleaned.
    • Permission tiers had gaps: newsletter staff could flip the site's tracking-consent mode and log retention (admin-only screens), inbox staff could purge submissions for good, any logged-in account could write one plugin preference. Aligned with what each grant promises. Custom CSS printed on the public site is now reserved to administrators.
    • Smaller: a re-signup could rewrite an unconfirmed subscriber's data before any confirmation (now staged until the link is clicked); the "form filled too fast" trap could be skipped; deleting stored files compared paths loosely; a few PHP handler extensions were missing from the upload denylist.

    ✨ New

    Popups that are just forms

    The one-popup-per-site setting is gone. Any subscribe form can show itself as a popup: a Popup section on the form's card, with a switch. So a popup has everything a form has — fields, style, custom CSS, defaults, captcha, consent box — and you edit it like any other form.

    • Where: kinds of pages (front page, posts, pages, archives, search, custom types), plus specific pages, page sections (a page and everything under it), post categories, and URL patterns.
    • Several popups can coexist, each with its own targeting. Where two would meet, the more specific one shows, and the Forms view tells you about the overlap.
    • When: after scrolling, after a delay (never under three seconds — a popup is never instantaneous), when the mouse leaves, or only on click; three presets; a warning when the combination would annoy visitors.
    • Preview here: the real popup over the admin page, without leaving the editor. Preview on the site still exists.
    • A page or category deleted after being picked is shown as such and simply never matches.
    • Your existing popup migrated onto the form it showed.

    The Newsletters dashboard is the statistics

    The old tiles page and the separate Statistics tab are one screen: at a glance, month by month, newsletter by newsletter, by list — and every section ends with the buttons to what it draws from (subscribers, newsletters in preparation, forms, lists, deliverability). Nothing sent yet? It says so and offers to write the first one. The toolkit's own dashboard shows the Newsletters panel even before the first send.

    Cards or list

    Newsletters and SMTP identities get a two-icon switch top right. List shows one row per item — status, name, the essentials, a date — and a click unfolds the full card. Remembered per person and per section.

    A default for every setting

    Every setting a form or a newsletter can carry now has a site-wide default. Subscribe forms get a Defaults button: success message, the messages for people already subscribed or who never confirmed, the page after confirming, custom CSS for every form. New newsletters are stamped with defaults from the settings: lists, signature, tracking, logging — changing a default never rewrites existing newsletters.

    Say it when someone is already subscribed

    A polite built-in message for people who sign up while already on the list (check the spam folder, add us to your contacts), and another for those who signed up but never confirmed (the confirmation email is sent again). Both customisable per form or for the whole site.

    About page

    Version, requirements, licence, links to the guides, the changelog, the source and the issue tracker — and credits. With thanks to Benoît for valuable feedback during the beta test.

    🐛 Fixed

    • Unticking Track opens or Track clicks on a newsletter did not hold.
    • A captcha next to the submit button now lines up with it, and hosted captchas (Turnstile, hCaptcha, reCAPTCHA) are never clipped when the column is narrow.
    • A send interrupted mid-batch (timeout, server error) left recipients stuck and the newsletter marked "sent" without them; they are picked up again. Two senders starting the same newsletter at once could zero its recipient count.
    • The privacy erasure silently failed for the second person erased on the same newsletter.
    • The Plugin data screen was unreadable in the dark theme.
    • The French now fits its fields — shorter labels, placeholders that end in "…" instead of being cut.
    • Deleting the plugin with "wipe" left user data, the plugin's posts and the upload folders behind; "archive" deleted definitions the kept tables needed.

    🧹 Under the hood

    Inline scripts moved to files, styles on design tokens, one shared delete dialog for forms, one escaper in the admin scripts, schema v24 with the indexes the send loop was missing, the popup and dashboard code documented in docs/.

    Downloads