From fef0766e675d660fa695bfee7a5d63e44c3c2847 Mon Sep 17 00:00:00 2001 From: Laurence Horrocks-Barlow Date: Sun, 17 May 2026 08:32:33 +0100 Subject: [PATCH] =?UTF-8?q?v1.0.1=20=E2=80=94=20security=20hardening,=20TE?= =?UTF-8?q?C=20integration=20fixes,=20and=20documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security: - Validate CSV upload MIME type server-side via finfo - Deliver import notices via per-user transient (prevents GET-param spoofing) - Sanitise translatable success string with wp_kses to block HTML injection - Switch sanitize_url to esc_url_raw; wp_kses_post to sanitize_textarea_field for plain-text bio Bug fixes: - Guard preg_replace null return in normalise_name() to prevent TypeError on PHP 8 - Replace generic save_post hook with save_post_tec_speaker / save_post_tribe_events so saves no longer need a manual revision check and cannot interact with TEC's own save_post handler at priority 15 TEC integration: - Check for tribe-select2 / tribe-select2-css handles first (TEC ships SelectWoo, not vanilla Select2); CDN was previously always loaded unnecessarily - Type-specific save hooks make event/speaker save paths explicit and independent Improvements: - Add register_activation_hook to flush rewrite rules on activation - Wrap instantiation in plugins_loaded so TEC is guaranteed loaded first - Show admin notice and skip TEC-specific hooks when TEC is inactive - Cap event picker query at PICKER_LIMIT = 200 (was unbounded -1) - Register front-end CSS via wp_add_inline_style on wp_enqueue_scripts - absint() on speaker IDs in option value attributes Documentation: - Write full README.md (was blank) - Add CHANGELOG.md with detailed 1.0.0 and 1.0.1 entries Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 46 ++++++ README.md | 98 +++++++++++- speakers-for-the-events-calendar.php | 222 ++++++++++++++++----------- 3 files changed, 276 insertions(+), 90 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f0a2463 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,46 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +--- + +## [1.0.1] — 2026-05-17 + +### Security + +- **CSV MIME validation** — the importer now checks the uploaded file's MIME type server-side via `finfo` before opening it. The browser-side `accept=".csv"` attribute is not a security control and was the only prior gate. +- **Admin notice spoofing** — import result banners (success/error) are now delivered via a per-user transient rather than raw GET parameters. A crafted URL can no longer display false success or error messages to an admin. +- **HTML in translatable string** — the success notice now passes through `wp_kses()` restricted to ``, preventing a tampered translation file from injecting arbitrary markup. +- **`esc_url_raw` over `sanitize_url`** — switched to the canonical WordPress function for storing URLs (functionally equivalent; `sanitize_url` is an undocumented alias). +- **`sanitize_textarea_field` for CSV bio** — the importer previously used `wp_kses_post()`, which permitted HTML in a field described to users as plain-text. Now consistently plain-text. + +### Bug fixes + +- **`normalise_name()` null return** — `preg_replace()` returns `null` on failure (e.g. backtracking limit, invalid UTF-8). Both calls now fall back to the input string via `?? $name`, preventing a `TypeError` on PHP 8. +- **Revision saves** — the generic `save_post` hook was replaced with type-specific `save_post_tec_speaker` and `save_post_tribe_events` hooks. `save_post_tribe_events` does not fire for revisions (which have post type `'revision'`), so the separate `wp_is_post_revision()` guard is no longer needed. + +### Integration + +- **Correct SelectWoo handles** — The Events Calendar ships SelectWoo (a Select2 fork) under the handles `tribe-select2` (JS) and `tribe-select2-css` (CSS), not `select2`. The admin script loader now checks for `tribe-select2` first, then `select2`, then falls back to a CDN copy. Previously, TEC's registration was never detected and the CDN was always loaded unnecessarily. +- **Type-specific save hooks** — replaced the single generic `save_post` hook (priority 10) with `save_post_tec_speaker` and `save_post_tribe_events`. TEC hooks its own meta save to `save_post` at priority 15; using type-specific hooks eliminates any ordering dependency and makes intent explicit. + +### Improvements + +- **Activation hook** — added `register_activation_hook` to register the CPT and flush rewrite rules on activation, fixing the "Speaker archive returns 404 until Permalinks are re-saved" issue on first install. +- **`plugins_loaded` wrapper** — the plugin is now instantiated inside `add_action('plugins_loaded', …)` instead of at file-include time, ensuring all plugins (including TEC) are loaded before the constructor runs. +- **TEC dependency notice** — if The Events Calendar is not active, an admin notice is shown. TEC-specific hooks (event display, event meta box) are skipped; the Speaker CPT, admin menu, and shortcode remain functional. +- **Admin picker cap** — the event speaker picker query is capped at 200 results (`PICKER_LIMIT` constant) instead of an unbounded `posts_per_page => -1`, preventing memory issues on large installs. +- **`absint()` on option values** — speaker IDs written into `